aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rwxr-xr-x.travis/install.sh10
-rw-r--r--CHANGELOG.rst44
-rw-r--r--README.rst4
-rw-r--r--cryptography/__about__.py2
-rw-r--r--cryptography/fernet.py14
-rw-r--r--cryptography/hazmat/backends/commoncrypto/backend.py20
-rw-r--r--cryptography/hazmat/backends/interfaces.py85
-rw-r--r--cryptography/hazmat/backends/multibackend.py79
-rw-r--r--cryptography/hazmat/backends/openssl/backend.py482
-rw-r--r--cryptography/hazmat/bindings/commoncrypto/binding.py7
-rw-r--r--cryptography/hazmat/bindings/commoncrypto/cf.py114
-rw-r--r--cryptography/hazmat/bindings/commoncrypto/secimport.py95
-rw-r--r--cryptography/hazmat/bindings/commoncrypto/secitem.py40
-rw-r--r--cryptography/hazmat/bindings/commoncrypto/seckey.py34
-rw-r--r--cryptography/hazmat/bindings/commoncrypto/seckeychain.py36
-rw-r--r--cryptography/hazmat/bindings/commoncrypto/sectransform.py79
-rw-r--r--cryptography/hazmat/bindings/openssl/aes.py6
-rw-r--r--cryptography/hazmat/bindings/openssl/asn1.py3
-rw-r--r--cryptography/hazmat/bindings/openssl/binding.py3
-rw-r--r--cryptography/hazmat/bindings/openssl/bio.py8
-rw-r--r--cryptography/hazmat/bindings/openssl/dh.py12
-rw-r--r--cryptography/hazmat/bindings/openssl/ec.py11
-rw-r--r--cryptography/hazmat/bindings/openssl/ecdh.py68
-rw-r--r--cryptography/hazmat/bindings/openssl/err.py2
-rw-r--r--cryptography/hazmat/bindings/openssl/opensslv.py2
-rw-r--r--cryptography/hazmat/bindings/openssl/ssl.py36
-rw-r--r--cryptography/hazmat/primitives/asymmetric/dsa.py37
-rw-r--r--cryptography/hazmat/primitives/asymmetric/ec.py69
-rw-r--r--cryptography/hazmat/primitives/asymmetric/padding.py16
-rw-r--r--cryptography/hazmat/primitives/asymmetric/rsa.py56
-rw-r--r--cryptography/hazmat/primitives/ciphers/algorithms.py2
-rw-r--r--cryptography/hazmat/primitives/ciphers/base.py24
-rw-r--r--cryptography/hazmat/primitives/ciphers/modes.py40
-rw-r--r--cryptography/hazmat/primitives/cmac.py20
-rw-r--r--cryptography/hazmat/primitives/constant_time.py6
-rw-r--r--cryptography/hazmat/primitives/hashes.py14
-rw-r--r--cryptography/hazmat/primitives/hmac.py18
-rw-r--r--cryptography/hazmat/primitives/interfaces.py60
-rw-r--r--cryptography/hazmat/primitives/kdf/hkdf.py72
-rw-r--r--cryptography/hazmat/primitives/kdf/pbkdf2.py22
-rw-r--r--cryptography/hazmat/primitives/padding.py24
-rw-r--r--cryptography/hazmat/primitives/serialization.py26
-rw-r--r--cryptography/hazmat/primitives/twofactor/hotp.py12
-rw-r--r--cryptography/hazmat/primitives/twofactor/totp.py4
-rw-r--r--cryptography/utils.py2
-rw-r--r--dev-requirements.txt1
-rw-r--r--docs/development/custom-vectors/cast5.rst6
-rw-r--r--docs/development/custom-vectors/idea.rst6
-rw-r--r--docs/development/custom-vectors/seed.rst6
-rw-r--r--docs/doing-a-release.rst3
-rw-r--r--docs/faq.rst29
-rw-r--r--docs/fernet.rst6
-rw-r--r--docs/hazmat/backends/commoncrypto.rst4
-rw-r--r--docs/hazmat/backends/interfaces.rst160
-rw-r--r--docs/hazmat/backends/openssl.rst14
-rw-r--r--docs/hazmat/primitives/asymmetric/dsa.rst101
-rw-r--r--docs/hazmat/primitives/asymmetric/ec.rst51
-rw-r--r--docs/hazmat/primitives/asymmetric/index.rst2
-rw-r--r--docs/hazmat/primitives/asymmetric/rsa.rst100
-rw-r--r--docs/hazmat/primitives/asymmetric/serialization.rst100
-rw-r--r--docs/hazmat/primitives/constant-time.rst2
-rw-r--r--docs/hazmat/primitives/cryptographic-hashes.rst1
-rw-r--r--docs/hazmat/primitives/interfaces.rst135
-rw-r--r--docs/hazmat/primitives/key-derivation-functions.rst107
-rw-r--r--docs/hazmat/primitives/mac/cmac.rst13
-rw-r--r--docs/hazmat/primitives/mac/hmac.rst7
-rw-r--r--docs/hazmat/primitives/mac/index.rst8
-rw-r--r--docs/hazmat/primitives/padding.rst1
-rw-r--r--docs/hazmat/primitives/symmetric-encryption.rst23
-rw-r--r--docs/installation.rst30
-rw-r--r--docs/spelling_wordlist.txt2
-rw-r--r--pytest.ini1
-rw-r--r--setup.py1
-rw-r--r--tasks.py2
-rw-r--r--tests/conftest.py8
-rw-r--r--tests/hazmat/backends/test_multibackend.py89
-rw-r--r--tests/hazmat/backends/test_openssl.py147
-rw-r--r--tests/hazmat/primitives/fixtures_rsa.py493
-rw-r--r--tests/hazmat/primitives/test_3des.py37
-rw-r--r--tests/hazmat/primitives/test_aes.py33
-rw-r--r--tests/hazmat/primitives/test_block.py8
-rw-r--r--tests/hazmat/primitives/test_dsa.py151
-rw-r--r--tests/hazmat/primitives/test_ec.py79
-rw-r--r--tests/hazmat/primitives/test_hkdf.py63
-rw-r--r--tests/hazmat/primitives/test_rsa.py565
-rw-r--r--tests/hazmat/primitives/test_serialization.py546
-rw-r--r--tests/hazmat/primitives/utils.py7
-rw-r--r--tests/test_utils.py30
-rw-r--r--tests/utils.py17
-rw-r--r--tox.ini9
-rw-r--r--vectors/cryptography_vectors/__about__.py2
-rw-r--r--vectors/cryptography_vectors/asymmetric/PKCS8/bad-encryption-oid.pem17
-rw-r--r--vectors/cryptography_vectors/asymmetric/PKCS8/bad-oid-dsa-key.pem9
-rw-r--r--vectors/cryptography_vectors/asymmetric/PKCS8/unenc-dsa-pkcs8.pem9
95 files changed, 4472 insertions, 593 deletions
diff --git a/.travis.yml b/.travis.yml
index b6fd6bd1..cacd1e27 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -39,9 +39,6 @@ notifications:
use_notice: true
skip_join: true
-# When building an exclude matrix on Travis you must supply the exact variable
-# combinations you want to exclude from your build matrix. There is no
-# (current) way to make this less verbose.
matrix:
exclude:
- os: osx
@@ -52,6 +49,7 @@ matrix:
- os: osx
env: TOX_ENV=py3pep8
compiler: clang
+
- os: linux
env: TOX_ENV=docs
compiler: clang
diff --git a/.travis/install.sh b/.travis/install.sh
index 79790050..e028033e 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -32,18 +32,18 @@ if [[ "$(uname -s)" == "Darwin" ]]; then
if which pyenv > /dev/null; then eval "$(pyenv init -)"; fi
case "${TOX_ENV}" in
py26)
- curl -O https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py
+ curl -O https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
sudo pip install virtualenv
;;
py27)
- curl -O https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py
+ curl -O https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
sudo pip install virtualenv
;;
pypy)
- pyenv install pypy-2.2.1
- pyenv global pypy-2.2.1
+ pyenv install pypy-2.3
+ pyenv global pypy-2.3
pip install virtualenv
;;
py32)
@@ -62,7 +62,7 @@ if [[ "$(uname -s)" == "Darwin" ]]; then
pip install virtualenv
;;
docs)
- curl -O https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py
+ curl -O https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
sudo pip install virtualenv
;;
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index e09fa5d6..13c62de5 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,19 +1,42 @@
Changelog
=========
-0.4 - `master`_
+0.5 - `master`_
~~~~~~~~~~~~~~~
.. note:: This version is not yet released and is under active development.
+* 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
+ :class:`~cryptography.hazmat.primitives.ciphers.algorithms.TripleDES` on
+ :doc:`/hazmat/backends/commoncrypto` and :doc:`/hazmat/backends/openssl`.
+* Added ``AES`` :class:`~cryptography.hazmat.primitives.ciphers.modes.CTR`
+ support to the OpenSSL backend when linked against 0.9.8.
+* Added
+ :class:`~cryptography.hazmat.backends.interfaces.PKCS8SerializationBackend`
+ and
+ :class:`~cryptography.hazmat.backends.interfaces.TraditionalOpenSSLSerializationBackend`
+ support to the :doc:`/hazmat/backends/openssl`.
+
+
+0.4 - 2014-05-03
+~~~~~~~~~~~~~~~~
+
* Deprecated ``salt_length`` on
:class:`~cryptography.hazmat.primitives.asymmetric.padding.MGF1` and added it
to :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`. It will be
removed from ``MGF1`` in two releases per our :doc:`/api-stability` policy.
* Added :class:`~cryptography.hazmat.primitives.ciphers.algorithms.SEED` support.
* Added :class:`~cryptography.hazmat.primitives.cmac.CMAC`.
-* Added decryption support to :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`
- and encryption support to :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`.
+* Added decryption support to
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`
+ and encryption support to
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`.
+* Added signature support to
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey`
+ and verification support to
+ :class:`~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey`.
0.3 - 2014-03-27
~~~~~~~~~~~~~~~~
@@ -21,8 +44,10 @@ Changelog
* Added :class:`~cryptography.hazmat.primitives.twofactor.hotp.HOTP`.
* Added :class:`~cryptography.hazmat.primitives.twofactor.totp.TOTP`.
* Added :class:`~cryptography.hazmat.primitives.ciphers.algorithms.IDEA` support.
-* Added signature support to :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`
- and verification support to :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`.
+* Added signature support to
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`
+ and verification support to
+ :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`.
* Moved test vectors to the new ``cryptography_vectors`` package.
0.2.2 - 2014-03-03
@@ -33,7 +58,8 @@ Changelog
0.2.1 - 2014-02-22
~~~~~~~~~~~~~~~~~~
-* Fix a bug where importing cryptography from multiple paths could cause initialization to fail.
+* Fix a bug where importing cryptography from multiple paths could cause
+ initialization to fail.
0.2 - 2014-02-20
~~~~~~~~~~~~~~~~
@@ -49,8 +75,10 @@ Changelog
* Added :class:`~cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC`.
* Added :class:`~cryptography.hazmat.primitives.kdf.hkdf.HKDF`.
* Added :doc:`/hazmat/backends/multibackend`.
-* Set default random for the :doc:`/hazmat/backends/openssl` to the OS random engine.
-* Added :class:`~cryptography.hazmat.primitives.ciphers.algorithms.CAST5` (CAST-128) support.
+* Set default random for the :doc:`/hazmat/backends/openssl` to the OS
+ random engine.
+* Added :class:`~cryptography.hazmat.primitives.ciphers.algorithms.CAST5`
+ (CAST-128) support.
0.1 - 2014-01-08
~~~~~~~~~~~~~~~~
diff --git a/README.rst b/README.rst
index ac7ec61a..5e1a82a8 100644
--- a/README.rst
+++ b/README.rst
@@ -1,6 +1,10 @@
Cryptography
============
+.. image:: https://pypip.in/version/cryptography/badge.svg
+ :target: https://pypi.python.org/pypi/cryptography/
+ :alt: Latest Version
+
.. image:: https://travis-ci.org/pyca/cryptography.svg?branch=master
:target: https://travis-ci.org/pyca/cryptography
diff --git a/cryptography/__about__.py b/cryptography/__about__.py
index ce0f32fc..ee53902b 100644
--- a/cryptography/__about__.py
+++ b/cryptography/__about__.py
@@ -22,7 +22,7 @@ __summary__ = ("cryptography is a package which provides cryptographic recipes"
" and primitives to Python developers.")
__uri__ = "https://github.com/pyca/cryptography"
-__version__ = "0.4.dev1"
+__version__ = "0.5.dev1"
__author__ = "The cryptography developers"
__email__ = "cryptography-dev@python.org"
diff --git a/cryptography/fernet.py b/cryptography/fernet.py
index 674ce8ae..cdb9bdca 100644
--- a/cryptography/fernet.py
+++ b/cryptography/fernet.py
@@ -43,7 +43,7 @@ class Fernet(object):
key = base64.urlsafe_b64decode(key)
if len(key) != 32:
raise ValueError(
- "Fernet key must be 32 url-safe base64-encoded bytes"
+ "Fernet key must be 32 url-safe base64-encoded bytes."
)
self._signing_key = key[:16]
@@ -60,10 +60,8 @@ class Fernet(object):
return self._encrypt_from_parts(data, current_time, iv)
def _encrypt_from_parts(self, data, current_time, iv):
- if isinstance(data, six.text_type):
- raise TypeError(
- "Unicode-objects must be encoded before encryption"
- )
+ if not isinstance(data, bytes):
+ raise TypeError("data must be bytes.")
padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(data) + padder.finalize()
@@ -82,10 +80,8 @@ class Fernet(object):
return base64.urlsafe_b64encode(basic_parts + hmac)
def decrypt(self, token, ttl=None):
- if isinstance(token, six.text_type):
- raise TypeError(
- "Unicode-objects must be encoded before decryption"
- )
+ if not isinstance(token, bytes):
+ raise TypeError("token must be bytes.")
current_time = int(time.time())
diff --git a/cryptography/hazmat/backends/commoncrypto/backend.py b/cryptography/hazmat/backends/commoncrypto/backend.py
index 4faca73e..213cbd8c 100644
--- a/cryptography/hazmat/backends/commoncrypto/backend.py
+++ b/cryptography/hazmat/backends/commoncrypto/backend.py
@@ -28,7 +28,7 @@ from cryptography.hazmat.primitives.ciphers.algorithms import (
AES, ARC4, Blowfish, CAST5, TripleDES
)
from cryptography.hazmat.primitives.ciphers.modes import (
- CBC, CFB, CTR, ECB, GCM, OFB
+ CBC, CFB, CFB8, CTR, ECB, GCM, OFB
)
@@ -154,7 +154,7 @@ class Backend(object):
def _register_cipher_adapter(self, cipher_cls, cipher_const, mode_cls,
mode_const):
if (cipher_cls, mode_cls) in self._cipher_registry:
- raise ValueError("Duplicate registration for: {0} {1}".format(
+ raise ValueError("Duplicate registration for: {0} {1}.".format(
cipher_cls, mode_cls)
)
self._cipher_registry[cipher_cls, mode_cls] = (cipher_const,
@@ -165,6 +165,7 @@ class Backend(object):
(CBC, self._lib.kCCModeCBC),
(ECB, self._lib.kCCModeECB),
(CFB, self._lib.kCCModeCFB),
+ (CFB8, self._lib.kCCModeCFB8),
(OFB, self._lib.kCCModeOFB),
(CTR, self._lib.kCCModeCTR),
(GCM, self._lib.kCCModeGCM),
@@ -178,6 +179,7 @@ class Backend(object):
for mode_cls, mode_const in [
(CBC, self._lib.kCCModeCBC),
(CFB, self._lib.kCCModeCFB),
+ (CFB8, self._lib.kCCModeCFB8),
(OFB, self._lib.kCCModeOFB),
]:
self._register_cipher_adapter(
@@ -226,7 +228,7 @@ class Backend(object):
# rdar://15589470
raise ValueError(
"The length of the provided data is not a multiple of "
- "the block length"
+ "the block length."
)
else:
raise InternalError(
@@ -264,7 +266,7 @@ class _CipherContext(object):
# This bug has been filed as rdar://15589470
self._bytes_processed = 0
if (isinstance(cipher, interfaces.BlockCipherAlgorithm) and not
- isinstance(mode, (OFB, CFB, CTR))):
+ isinstance(mode, (OFB, CFB, CFB8, CTR))):
self._byte_block_size = cipher.block_size // 8
else:
self._byte_block_size = 1
@@ -275,7 +277,7 @@ class _CipherContext(object):
except KeyError:
raise UnsupportedAlgorithm(
"cipher {0} in {1} mode is not supported "
- "by this backend".format(
+ "by this backend.".format(
cipher.name, mode.name if mode else mode),
_Reasons.UNSUPPORTED_CIPHER
)
@@ -322,7 +324,7 @@ class _CipherContext(object):
if self._bytes_processed % self._byte_block_size:
raise ValueError(
"The length of the provided data is not a multiple of "
- "the block length"
+ "the block length."
)
buf = self._backend._ffi.new("unsigned char[]", self._byte_block_size)
outlen = self._backend._ffi.new("size_t *")
@@ -349,7 +351,7 @@ class _GCMCipherContext(object):
except KeyError:
raise UnsupportedAlgorithm(
"cipher {0} in {1} mode is not supported "
- "by this backend".format(
+ "by this backend.".format(
cipher.name, mode.name if mode else mode),
_Reasons.UNSUPPORTED_CIPHER
)
@@ -423,7 +425,7 @@ class _HashContext(object):
methods = self._backend._hash_mapping[self.algorithm.name]
except KeyError:
raise UnsupportedAlgorithm(
- "{0} is not a supported hash on this backend".format(
+ "{0} is not a supported hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
@@ -467,7 +469,7 @@ class _HMACContext(object):
alg = self._backend._supported_hmac_algorithms[algorithm.name]
except KeyError:
raise UnsupportedAlgorithm(
- "{0} is not a supported HMAC hash on this backend".format(
+ "{0} is not a supported HMAC hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py
index aaaca5e2..ba02bbd2 100644
--- a/cryptography/hazmat/backends/interfaces.py
+++ b/cryptography/hazmat/backends/interfaces.py
@@ -129,6 +129,19 @@ class RSABackend(object):
Returns encrypted bytes.
"""
+ @abc.abstractmethod
+ def rsa_padding_supported(self, padding):
+ """
+ Returns True if the backend supports the given padding options.
+ """
+
+ @abc.abstractmethod
+ def generate_rsa_parameters_supported(self, public_exponent, key_size):
+ """
+ Returns True if the backend supports the given parameters for key
+ generation.
+ """
+
@six.add_metaclass(abc.ABCMeta)
class DSABackend(object):
@@ -145,6 +158,32 @@ class DSABackend(object):
a DSAParameters object.
"""
+ @abc.abstractmethod
+ def create_dsa_signature_ctx(self, private_key, algorithm):
+ """
+ Returns an object conforming to the AsymmetricSignatureContext
+ interface.
+ """
+
+ @abc.abstractmethod
+ def create_dsa_verification_ctx(self, public_key, signature, algorithm):
+ """
+ Returns an object conforming to the AsymmetricVerificationContext
+ interface.
+ """
+
+ @abc.abstractmethod
+ def dsa_hash_supported(self, algorithm):
+ """
+ Return True if the hash algorithm is supported by the backend for DSA.
+ """
+
+ @abc.abstractmethod
+ def dsa_parameters_supported(self, p, q, g):
+ """
+ Return True if the parameters are supported by the backend for DSA.
+ """
+
@six.add_metaclass(abc.ABCMeta)
class TraditionalOpenSSLSerializationBackend(object):
@@ -157,6 +196,16 @@ class TraditionalOpenSSLSerializationBackend(object):
@six.add_metaclass(abc.ABCMeta)
+class PKCS8SerializationBackend(object):
+ @abc.abstractmethod
+ def load_pkcs8_pem_private_key(self, data, password):
+ """
+ Load a private key from PEM encoded data, using password if the data
+ is encrypted.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
class CMACBackend(object):
@abc.abstractmethod
def cmac_algorithm_supported(self, algorithm):
@@ -169,3 +218,39 @@ class CMACBackend(object):
"""
Create a CMACContext for calculating a message authentication code.
"""
+
+
+@six.add_metaclass(abc.ABCMeta)
+class EllipticCurveBackend(object):
+ @abc.abstractmethod
+ def elliptic_curve_signature_algorithm_supported(
+ self, signature_algorithm, curve
+ ):
+ """
+ Returns True if the backend supports the named elliptic curve with the
+ specified signature algorithm.
+ """
+
+ @abc.abstractmethod
+ def elliptic_curve_supported(self, curve):
+ """
+ Returns True if the backend supports the named elliptic curve.
+ """
+
+ @abc.abstractmethod
+ def generate_elliptic_curve_private_key(self, curve):
+ """
+ Return an object conforming to the EllipticCurvePrivateKey interface.
+ """
+
+ @abc.abstractmethod
+ def elliptic_curve_public_key_from_numbers(self, numbers):
+ """
+ Return an EllipticCurvePublicKey provider using the given numbers.
+ """
+
+ @abc.abstractmethod
+ def elliptic_curve_private_key_from_numbers(self, numbers):
+ """
+ Return an EllipticCurvePublicKey provider using the given numbers.
+ """
diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py
index 981a60bd..b4cb6889 100644
--- a/cryptography/hazmat/backends/multibackend.py
+++ b/cryptography/hazmat/backends/multibackend.py
@@ -52,7 +52,7 @@ class MultiBackend(object):
except UnsupportedAlgorithm:
pass
raise UnsupportedAlgorithm(
- "cipher {0} in {1} mode is not supported by this backend".format(
+ "cipher {0} in {1} mode is not supported by this backend.".format(
algorithm.name, mode.name if mode else mode),
_Reasons.UNSUPPORTED_CIPHER
)
@@ -64,7 +64,7 @@ class MultiBackend(object):
except UnsupportedAlgorithm:
pass
raise UnsupportedAlgorithm(
- "cipher {0} in {1} mode is not supported by this backend".format(
+ "cipher {0} in {1} mode is not supported by this backend.".format(
algorithm.name, mode.name if mode else mode),
_Reasons.UNSUPPORTED_CIPHER
)
@@ -82,7 +82,7 @@ class MultiBackend(object):
except UnsupportedAlgorithm:
pass
raise UnsupportedAlgorithm(
- "{0} is not a supported hash on this backend".format(
+ "{0} is not a supported hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
@@ -100,7 +100,7 @@ class MultiBackend(object):
except UnsupportedAlgorithm:
pass
raise UnsupportedAlgorithm(
- "{0} is not a supported hash on this backend".format(
+ "{0} is not a supported hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
@@ -121,7 +121,7 @@ class MultiBackend(object):
except UnsupportedAlgorithm:
pass
raise UnsupportedAlgorithm(
- "{0} is not a supported hash on this backend".format(
+ "{0} is not a supported hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
@@ -129,13 +129,21 @@ class MultiBackend(object):
def generate_rsa_private_key(self, public_exponent, key_size):
for b in self._filtered_backends(RSABackend):
return b.generate_rsa_private_key(public_exponent, key_size)
- raise UnsupportedAlgorithm("RSA is not supported by the backend",
+ raise UnsupportedAlgorithm("RSA is not supported by the backend.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def generate_rsa_parameters_supported(self, public_exponent, key_size):
+ for b in self._filtered_backends(RSABackend):
+ return b.generate_rsa_parameters_supported(
+ public_exponent, key_size
+ )
+ raise UnsupportedAlgorithm("RSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def create_rsa_signature_ctx(self, private_key, padding, algorithm):
for b in self._filtered_backends(RSABackend):
return b.create_rsa_signature_ctx(private_key, padding, algorithm)
- raise UnsupportedAlgorithm("RSA is not supported by the backend",
+ raise UnsupportedAlgorithm("RSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def create_rsa_verification_ctx(self, public_key, signature, padding,
@@ -143,19 +151,68 @@ class MultiBackend(object):
for b in self._filtered_backends(RSABackend):
return b.create_rsa_verification_ctx(public_key, signature,
padding, algorithm)
- raise UnsupportedAlgorithm("RSA is not supported by the backend",
+ raise UnsupportedAlgorithm("RSA is not supported by the backend.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def mgf1_hash_supported(self, algorithm):
+ for b in self._filtered_backends(RSABackend):
+ return b.mgf1_hash_supported(algorithm)
+ raise UnsupportedAlgorithm("RSA is not supported by the backend.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def decrypt_rsa(self, private_key, ciphertext, padding):
+ for b in self._filtered_backends(RSABackend):
+ return b.decrypt_rsa(private_key, ciphertext, padding)
+ raise UnsupportedAlgorithm("RSA is not supported by the backend.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def encrypt_rsa(self, public_key, plaintext, padding):
+ for b in self._filtered_backends(RSABackend):
+ return b.encrypt_rsa(public_key, plaintext, padding)
+ raise UnsupportedAlgorithm("RSA is not supported by the backend.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def rsa_padding_supported(self, padding):
+ for b in self._filtered_backends(RSABackend):
+ return b.rsa_padding_supported(padding)
+ raise UnsupportedAlgorithm("RSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def generate_dsa_parameters(self, key_size):
for b in self._filtered_backends(DSABackend):
return b.generate_dsa_parameters(key_size)
- raise UnsupportedAlgorithm("DSA is not supported by the backend",
+ raise UnsupportedAlgorithm("DSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def generate_dsa_private_key(self, parameters):
for b in self._filtered_backends(DSABackend):
return b.generate_dsa_private_key(parameters)
- raise UnsupportedAlgorithm("DSA is not supported by the backend",
+ raise UnsupportedAlgorithm("DSA is not supported by the backend.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def create_dsa_verification_ctx(self, public_key, signature, algorithm):
+ for b in self._filtered_backends(DSABackend):
+ return b.create_dsa_verification_ctx(public_key, signature,
+ algorithm)
+ raise UnsupportedAlgorithm("DSA is not supported by the backend.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def create_dsa_signature_ctx(self, private_key, algorithm):
+ for b in self._filtered_backends(DSABackend):
+ return b.create_dsa_signature_ctx(private_key, algorithm)
+ raise UnsupportedAlgorithm("DSA is not supported by the backend.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def dsa_hash_supported(self, algorithm):
+ for b in self._filtered_backends(DSABackend):
+ return b.dsa_hash_supported(algorithm)
+ raise UnsupportedAlgorithm("DSA is not supported by the backend.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def dsa_parameters_supported(self, p, q, g):
+ for b in self._filtered_backends(DSABackend):
+ return b.dsa_parameters_supported(p, q, g)
+ raise UnsupportedAlgorithm("DSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def cmac_algorithm_supported(self, algorithm):
@@ -170,5 +227,5 @@ class MultiBackend(object):
return b.create_cmac_ctx(algorithm)
except UnsupportedAlgorithm:
pass
- raise UnsupportedAlgorithm("This backend does not support CMAC",
+ raise UnsupportedAlgorithm("This backend does not support CMAC.",
_Reasons.UNSUPPORTED_CIPHER)
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index f9154f3b..4112f0e5 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -26,7 +26,8 @@ from cryptography.exceptions import (
)
from cryptography.hazmat.backends.interfaces import (
CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend,
- PBKDF2HMACBackend, RSABackend
+ PBKDF2HMACBackend, PKCS8SerializationBackend, RSABackend,
+ TraditionalOpenSSLSerializationBackend
)
from cryptography.hazmat.bindings.openssl.binding import Binding
from cryptography.hazmat.primitives import hashes, interfaces
@@ -38,10 +39,11 @@ from cryptography.hazmat.primitives.ciphers.algorithms import (
AES, ARC4, Blowfish, CAST5, Camellia, IDEA, SEED, TripleDES
)
from cryptography.hazmat.primitives.ciphers.modes import (
- CBC, CFB, CTR, ECB, GCM, OFB
+ CBC, CFB, CFB8, CTR, ECB, GCM, OFB
)
+_MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"])
_OpenSSLError = collections.namedtuple("_OpenSSLError",
["code", "lib", "func", "reason"])
@@ -53,6 +55,8 @@ _OpenSSLError = collections.namedtuple("_OpenSSLError",
@utils.register_interface(HMACBackend)
@utils.register_interface(PBKDF2HMACBackend)
@utils.register_interface(RSABackend)
+@utils.register_interface(TraditionalOpenSSLSerializationBackend)
+@utils.register_interface(PKCS8SerializationBackend)
class Backend(object):
"""
OpenSSL API binding interfaces.
@@ -112,11 +116,14 @@ class Backend(object):
def openssl_version_text(self):
"""
- Friendly string name of linked OpenSSL.
+ Friendly string name of the loaded OpenSSL library. This is not
+ necessarily the same version as it was compiled against.
Example: OpenSSL 1.0.1e 11 Feb 2013
"""
- return self._ffi.string(self._lib.OPENSSL_VERSION_TEXT).decode("ascii")
+ return self._ffi.string(
+ self._lib.SSLeay_version(self._lib.SSLEAY_VERSION)
+ ).decode("ascii")
def create_hmac_ctx(self, key, algorithm):
return _HMACContext(self, key, algorithm)
@@ -132,6 +139,14 @@ class Backend(object):
return _HashContext(self, algorithm)
def cipher_supported(self, cipher, mode):
+ if self._evp_cipher_supported(cipher, mode):
+ return True
+ elif isinstance(mode, CTR) and isinstance(cipher, AES):
+ return True
+ else:
+ return False
+
+ def _evp_cipher_supported(self, cipher, mode):
try:
adapter = self._cipher_registry[type(cipher), type(mode)]
except KeyError:
@@ -141,22 +156,25 @@ class Backend(object):
def register_cipher_adapter(self, cipher_cls, mode_cls, adapter):
if (cipher_cls, mode_cls) in self._cipher_registry:
- raise ValueError("Duplicate registration for: {0} {1}".format(
+ raise ValueError("Duplicate registration for: {0} {1}.".format(
cipher_cls, mode_cls)
)
self._cipher_registry[cipher_cls, mode_cls] = adapter
def _register_default_ciphers(self):
- for cipher_cls, mode_cls in itertools.product(
- [AES, Camellia],
- [CBC, CTR, ECB, OFB, CFB],
- ):
+ for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8]:
self.register_cipher_adapter(
- cipher_cls,
+ AES,
mode_cls,
GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
)
- for mode_cls in [CBC, CFB, OFB]:
+ for mode_cls in [CBC, CTR, ECB, OFB, CFB]:
+ self.register_cipher_adapter(
+ Camellia,
+ mode_cls,
+ GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
+ )
+ for mode_cls in [CBC, CFB, CFB8, OFB]:
self.register_cipher_adapter(
TripleDES,
mode_cls,
@@ -195,10 +213,24 @@ class Backend(object):
)
def create_symmetric_encryption_ctx(self, cipher, mode):
- return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)
+ if (isinstance(mode, CTR) and isinstance(cipher, AES)
+ and not self._evp_cipher_supported(cipher, mode)):
+ # This is needed to provide support for AES CTR mode in OpenSSL
+ # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5
+ # extended life ends 2020).
+ return _AESCTRCipherContext(self, cipher, mode)
+ else:
+ return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)
def create_symmetric_decryption_ctx(self, cipher, mode):
- return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)
+ if (isinstance(mode, CTR) and isinstance(cipher, AES)
+ and not self._evp_cipher_supported(cipher, mode)):
+ # This is needed to provide support for AES CTR mode in OpenSSL
+ # 0.9.8. It can be removed when we drop 0.9.8 support (RHEL 5
+ # extended life ends 2020).
+ return _AESCTRCipherContext(self, cipher, mode)
+ else:
+ return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)
def pbkdf2_hmac_supported(self, algorithm):
if self._lib.Cryptography_HAS_PBKDF2_HMAC:
@@ -231,7 +263,7 @@ class Backend(object):
if not isinstance(algorithm, hashes.SHA1):
raise UnsupportedAlgorithm(
"This version of OpenSSL only supports PBKDF2HMAC with "
- "SHA1",
+ "SHA1.",
_Reasons.UNSUPPORTED_HASH
)
res = self._lib.PKCS5_PBKDF2_HMAC_SHA1(
@@ -269,7 +301,7 @@ class Backend(object):
def _unknown_error(self, error):
return InternalError(
"Unknown error code {0} from OpenSSL, "
- "you should probably file a bug. {1}".format(
+ "you should probably file a bug. {1}.".format(
error.code, self._err_string(error.code)
)
)
@@ -325,14 +357,7 @@ class Backend(object):
return bn_ptr[0]
def generate_rsa_private_key(self, public_exponent, key_size):
- if public_exponent < 3:
- raise ValueError("public_exponent must be >= 3")
-
- if public_exponent & 1 == 0:
- raise ValueError("public_exponent must be odd")
-
- if key_size < 512:
- raise ValueError("key_size must be at least 512-bits")
+ rsa._verify_rsa_parameters(public_exponent, key_size)
ctx = self._lib.RSA_new()
assert ctx != self._ffi.NULL
@@ -348,6 +373,10 @@ class Backend(object):
return self._rsa_cdata_to_private_key(ctx)
+ def generate_rsa_parameters_supported(self, public_exponent, key_size):
+ return (public_exponent >= 3 and public_exponent & 1 != 0 and
+ key_size >= 512)
+
def _new_evp_pkey(self):
evp_pkey = self._lib.EVP_PKEY_new()
assert evp_pkey != self._ffi.NULL
@@ -371,6 +400,51 @@ class Backend(object):
return evp_pkey
+ def _bytes_to_bio(self, data):
+ """
+ Return a _MemoryBIO namedtuple of (BIO, char*).
+
+ The char* is the storage for the BIO and it must stay alive until the
+ BIO is finished with.
+ """
+ data_char_p = backend._ffi.new("char[]", data)
+ bio = backend._lib.BIO_new_mem_buf(
+ data_char_p, len(data)
+ )
+ assert bio != self._ffi.NULL
+
+ return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_char_p)
+
+ def _evp_pkey_to_private_key(self, evp_pkey):
+ """
+ Return the appropriate type of PrivateKey given an evp_pkey cdata
+ pointer.
+ """
+
+ type = evp_pkey.type
+
+ if type == self._lib.EVP_PKEY_RSA:
+ rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
+ assert rsa_cdata != self._ffi.NULL
+ rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
+ return self._rsa_cdata_to_private_key(rsa_cdata)
+ elif type == self._lib.EVP_PKEY_DSA:
+ dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey)
+ assert dsa_cdata != self._ffi.NULL
+ dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free)
+ return self._dsa_cdata_to_private_key(dsa_cdata)
+ else:
+ raise UnsupportedAlgorithm("Unsupported key type.")
+
+ def _dsa_cdata_to_private_key(self, cdata):
+ return dsa.DSAPrivateKey(
+ modulus=self._bn_to_int(cdata.p),
+ subgroup_order=self._bn_to_int(cdata.q),
+ generator=self._bn_to_int(cdata.g),
+ x=self._bn_to_int(cdata.priv_key),
+ y=self._bn_to_int(cdata.pub_key)
+ )
+
def _rsa_cdata_to_private_key(self, cdata):
return rsa.RSAPrivateKey(
p=self._bn_to_int(cdata.p),
@@ -383,6 +457,37 @@ class Backend(object):
modulus=self._bn_to_int(cdata.n),
)
+ def _pem_password_cb(self, password):
+ """
+ Generate a pem_password_cb function pointer that copied the password to
+ OpenSSL as required and returns the number of bytes copied.
+
+ typedef int pem_password_cb(char *buf, int size,
+ int rwflag, void *userdata);
+
+ Useful for decrypting PKCS8 files and so on.
+
+ Returns a tuple of (cdata function pointer, callback function).
+ """
+
+ def pem_password_cb(buf, size, writing, userdata):
+ pem_password_cb.called += 1
+
+ if not password or len(password) >= size:
+ return 0
+ else:
+ pw_buf = self._ffi.buffer(buf, size)
+ pw_buf[:len(password)] = password
+ return len(password)
+
+ pem_password_cb.called = 0
+
+ return (
+ self._ffi.callback("int (char *, int, int, void *)",
+ pem_password_cb),
+ pem_password_cb
+ )
+
def _rsa_cdata_from_private_key(self, private_key):
# Does not GC the RSA cdata. You *must* make sure it's freed
# correctly yourself!
@@ -428,16 +533,26 @@ class Backend(object):
else:
return isinstance(algorithm, hashes.SHA1)
+ def rsa_padding_supported(self, padding):
+ if isinstance(padding, PKCS1v15):
+ return True
+ elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1):
+ return self.mgf1_hash_supported(padding._mgf._algorithm)
+ elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1):
+ return isinstance(padding._mgf._algorithm, hashes.SHA1)
+ else:
+ return False
+
def generate_dsa_parameters(self, key_size):
if key_size not in (1024, 2048, 3072):
raise ValueError(
- "Key size must be 1024 or 2048 or 3072 bits")
+ "Key size must be 1024 or 2048 or 3072 bits.")
if (self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f and
key_size > 1024):
raise ValueError(
"Key size must be 1024 because OpenSSL < 1.0.0 doesn't "
- "support larger key sizes")
+ "support larger key sizes.")
ctx = self._lib.DSA_new()
assert ctx != self._ffi.NULL
@@ -474,6 +589,51 @@ class Backend(object):
y=self._bn_to_int(ctx.pub_key)
)
+ def create_dsa_signature_ctx(self, private_key, algorithm):
+ return _DSASignatureContext(self, private_key, algorithm)
+
+ def create_dsa_verification_ctx(self, public_key, signature,
+ algorithm):
+ return _DSAVerificationContext(self, public_key, signature,
+ algorithm)
+
+ def _dsa_cdata_from_public_key(self, public_key):
+ # Does not GC the DSA cdata. You *must* make sure it's freed
+ # correctly yourself!
+ ctx = self._lib.DSA_new()
+ assert ctx != self._ffi.NULL
+ parameters = public_key.parameters()
+ ctx.p = self._int_to_bn(parameters.p)
+ ctx.q = self._int_to_bn(parameters.q)
+ ctx.g = self._int_to_bn(parameters.g)
+ ctx.pub_key = self._int_to_bn(public_key.y)
+ return ctx
+
+ def _dsa_cdata_from_private_key(self, private_key):
+ # Does not GC the DSA cdata. You *must* make sure it's freed
+ # correctly yourself!
+ ctx = self._lib.DSA_new()
+ assert ctx != self._ffi.NULL
+ parameters = private_key.parameters()
+ ctx.p = self._int_to_bn(parameters.p)
+ ctx.q = self._int_to_bn(parameters.q)
+ ctx.g = self._int_to_bn(parameters.g)
+ ctx.priv_key = self._int_to_bn(private_key.x)
+ ctx.pub_key = self._int_to_bn(private_key.y)
+ return ctx
+
+ def dsa_hash_supported(self, algorithm):
+ if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f:
+ return isinstance(algorithm, hashes.SHA1)
+ else:
+ return self.hash_supported(algorithm)
+
+ def dsa_parameters_supported(self, p, q, g):
+ if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f:
+ return (utils.bit_length(p) <= 1024 and utils.bit_length(q) <= 160)
+ else:
+ return True
+
def decrypt_rsa(self, private_key, ciphertext, padding):
key_size_bytes = int(math.ceil(private_key.key_size / 8.0))
if key_size_bytes != len(ciphertext):
@@ -491,28 +651,28 @@ class Backend(object):
padding_enum = self._lib.RSA_PKCS1_OAEP_PADDING
if not isinstance(padding._mgf, MGF1):
raise UnsupportedAlgorithm(
- "Only MGF1 is supported by this backend",
+ "Only MGF1 is supported by this backend.",
_Reasons.UNSUPPORTED_MGF
)
if not isinstance(padding._mgf._algorithm, hashes.SHA1):
raise UnsupportedAlgorithm(
"This backend supports only SHA1 inside MGF1 when "
- "using OAEP",
+ "using OAEP.",
_Reasons.UNSUPPORTED_HASH
)
if padding._label is not None and padding._label != b"":
- raise ValueError("This backend does not support OAEP labels")
+ raise ValueError("This backend does not support OAEP labels.")
if not isinstance(padding._algorithm, hashes.SHA1):
raise UnsupportedAlgorithm(
- "This backend only supports SHA1 when using OAEP",
+ "This backend only supports SHA1 when using OAEP.",
_Reasons.UNSUPPORTED_HASH
)
else:
raise UnsupportedAlgorithm(
- "{0} is not supported by this backend".format(
+ "{0} is not supported by this backend.".format(
padding.name
),
_Reasons.UNSUPPORTED_PADDING
@@ -592,14 +752,14 @@ class Backend(object):
self._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE)
raise ValueError(
"Data too long for key size. Encrypt less data or use a "
- "larger key size"
+ "larger key size."
)
else:
assert (
errors[0].reason == self._lib.RSA_R_BLOCK_TYPE_IS_NOT_01 or
errors[0].reason == self._lib.RSA_R_BLOCK_TYPE_IS_NOT_02
)
- raise ValueError("Decryption failed")
+ raise ValueError("Decryption failed.")
def cmac_algorithm_supported(self, algorithm):
return (
@@ -611,6 +771,107 @@ class Backend(object):
def create_cmac_ctx(self, algorithm):
return _CMACContext(self, algorithm)
+ def load_traditional_openssl_pem_private_key(self, data, password):
+ # OpenSSLs API for loading PKCS#8 certs can also load the traditional
+ # format so we just use that for both of them.
+
+ return self.load_pkcs8_pem_private_key(data, password)
+
+ def load_pkcs8_pem_private_key(self, data, password):
+ mem_bio = self._bytes_to_bio(data)
+
+ password_callback, password_func = self._pem_password_cb(password)
+
+ evp_pkey = self._lib.PEM_read_bio_PrivateKey(
+ mem_bio.bio,
+ self._ffi.NULL,
+ password_callback,
+ self._ffi.NULL
+ )
+
+ if evp_pkey == self._ffi.NULL:
+ errors = self._consume_errors()
+ if not errors:
+ raise ValueError("Could not unserialize key data.")
+
+ if (
+ errors[0][1:] == (
+ self._lib.ERR_LIB_PEM,
+ self._lib.PEM_F_PEM_DO_HEADER,
+ self._lib.PEM_R_BAD_PASSWORD_READ
+ )
+ ) or (
+ errors[0][1:] == (
+ self._lib.ERR_LIB_PEM,
+ self._lib.PEM_F_PEM_READ_BIO_PRIVATEKEY,
+ self._lib.PEM_R_BAD_PASSWORD_READ
+ )
+ ):
+ assert not password
+ raise TypeError(
+ "Password was not given but private key is encrypted.")
+
+ elif errors[0][1:] == (
+ self._lib.ERR_LIB_EVP,
+ self._lib.EVP_F_EVP_DECRYPTFINAL_EX,
+ self._lib.EVP_R_BAD_DECRYPT
+ ):
+ raise ValueError(
+ "Bad decrypt. Incorrect password?"
+ )
+
+ elif errors[0][1:] in (
+ (
+ self._lib.ERR_LIB_PEM,
+ self._lib.PEM_F_PEM_GET_EVP_CIPHER_INFO,
+ self._lib.PEM_R_UNSUPPORTED_ENCRYPTION
+ ),
+
+ (
+ self._lib.ERR_LIB_EVP,
+ self._lib.EVP_F_EVP_PBE_CIPHERINIT,
+ self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM
+ )
+ ):
+ raise UnsupportedAlgorithm(
+ "PEM data is encrypted with an unsupported cipher",
+ _Reasons.UNSUPPORTED_CIPHER
+ )
+
+ elif any(
+ error[1:] == (
+ self._lib.ERR_LIB_EVP,
+ self._lib.EVP_F_EVP_PKCS82PKEY,
+ self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM
+ )
+ for error in errors
+ ):
+ raise UnsupportedAlgorithm(
+ "Unsupported public key algorithm.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ )
+
+ else:
+ assert errors[0][1] in (
+ self._lib.ERR_LIB_EVP,
+ self._lib.ERR_LIB_PEM,
+ self._lib.ERR_LIB_ASN1,
+ )
+ raise ValueError("Could not unserialize key data.")
+
+ evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
+
+ if password is not None and password_func.called == 0:
+ raise TypeError(
+ "Password was given but private key is not encrypted.")
+
+ assert (
+ (password is not None and password_func.called == 1) or
+ password is None
+ )
+
+ return self._evp_pkey_to_private_key(evp_pkey)
+
class GetCipherByName(object):
def __init__(self, fmt):
@@ -651,7 +912,7 @@ class _CipherContext(object):
except KeyError:
raise UnsupportedAlgorithm(
"cipher {0} in {1} mode is not supported "
- "by this backend".format(
+ "by this backend.".format(
cipher.name, mode.name if mode else mode),
_Reasons.UNSUPPORTED_CIPHER
)
@@ -660,7 +921,7 @@ class _CipherContext(object):
if evp_cipher == self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
"cipher {0} in {1} mode is not supported "
- "by this backend".format(
+ "by this backend.".format(
cipher.name, mode.name if mode else mode),
_Reasons.UNSUPPORTED_CIPHER
)
@@ -786,6 +1047,41 @@ class _CipherContext(object):
return self._tag
+@utils.register_interface(interfaces.CipherContext)
+class _AESCTRCipherContext(object):
+ """
+ This is needed to provide support for AES CTR mode in OpenSSL 0.9.8. It can
+ be removed when we drop 0.9.8 support (RHEL5 extended life ends 2020).
+ """
+ def __init__(self, backend, cipher, mode):
+ self._backend = backend
+
+ self._key = self._backend._ffi.new("AES_KEY *")
+ assert self._key != self._backend._ffi.NULL
+ res = self._backend._lib.AES_set_encrypt_key(
+ cipher.key, len(cipher.key) * 8, self._key
+ )
+ assert res == 0
+ self._ecount = self._backend._ffi.new("char[]", 16)
+ self._nonce = self._backend._ffi.new("char[16]", mode.nonce)
+ self._num = self._backend._ffi.new("unsigned int *", 0)
+
+ def update(self, data):
+ buf = self._backend._ffi.new("unsigned char[]", len(data))
+ self._backend._lib.AES_ctr128_encrypt(
+ data, buf, len(data), self._key, self._nonce,
+ self._ecount, self._num
+ )
+ return self._backend._ffi.buffer(buf)[:]
+
+ def finalize(self):
+ self._key = None
+ self._ecount = None
+ self._nonce = None
+ self._num = None
+ return b""
+
+
@utils.register_interface(interfaces.HashContext)
class _HashContext(object):
def __init__(self, backend, algorithm, ctx=None):
@@ -801,7 +1097,7 @@ class _HashContext(object):
algorithm.name.encode("ascii"))
if evp_md == self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
- "{0} is not a supported hash on this backend".format(
+ "{0} is not a supported hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
@@ -852,7 +1148,7 @@ class _HMACContext(object):
algorithm.name.encode('ascii'))
if evp_md == self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
- "{0} is not a supported hash on this backend".format(
+ "{0} is not a supported hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
@@ -921,7 +1217,7 @@ class _RSASignatureContext(object):
if not isinstance(padding, interfaces.AsymmetricPadding):
raise TypeError(
- "Expected provider of interfaces.AsymmetricPadding")
+ "Expected provider of interfaces.AsymmetricPadding.")
if isinstance(padding, PKCS1v15):
if self._backend._lib.Cryptography_HAS_PKEY_CTX:
@@ -932,7 +1228,7 @@ class _RSASignatureContext(object):
elif isinstance(padding, PSS):
if not isinstance(padding._mgf, MGF1):
raise UnsupportedAlgorithm(
- "Only MGF1 is supported by this backend",
+ "Only MGF1 is supported by this backend.",
_Reasons.UNSUPPORTED_MGF
)
@@ -957,24 +1253,18 @@ class _RSASignatureContext(object):
self._finalize_method = self._finalize_pss
else:
raise UnsupportedAlgorithm(
- "{0} is not supported by this backend".format(padding.name),
+ "{0} is not supported by this backend.".format(padding.name),
_Reasons.UNSUPPORTED_PADDING
)
self._padding = padding
self._algorithm = algorithm
- self._hash_ctx = _HashContext(backend, self._algorithm)
+ self._hash_ctx = hashes.Hash(self._algorithm, self._backend)
def update(self, data):
- if self._hash_ctx is None:
- raise AlreadyFinalized("Context has already been finalized")
-
self._hash_ctx.update(data)
def finalize(self):
- if self._hash_ctx is None:
- raise AlreadyFinalized("Context has already been finalized")
-
evp_pkey = self._backend._rsa_private_key_to_evp_pkey(
self._private_key)
@@ -1023,7 +1313,6 @@ class _RSASignatureContext(object):
)
assert res > 0
data_to_sign = self._hash_ctx.finalize()
- self._hash_ctx = None
buflen = self._backend._ffi.new("size_t *")
res = self._backend._lib.EVP_PKEY_sign(
pkey_ctx,
@@ -1053,16 +1342,18 @@ class _RSASignatureContext(object):
return self._backend._ffi.buffer(buf)[:]
def _finalize_pkcs1(self, evp_pkey, pkey_size, evp_md):
+ if self._hash_ctx._ctx is None:
+ raise AlreadyFinalized("Context has already been finalized.")
+
sig_buf = self._backend._ffi.new("char[]", pkey_size)
sig_len = self._backend._ffi.new("unsigned int *")
res = self._backend._lib.EVP_SignFinal(
- self._hash_ctx._ctx,
+ self._hash_ctx._ctx._ctx,
sig_buf,
sig_len,
evp_pkey
)
self._hash_ctx.finalize()
- self._hash_ctx = None
if res == 0:
errors = self._backend._consume_errors()
assert errors[0].lib == self._backend._lib.ERR_LIB_RSA
@@ -1075,7 +1366,6 @@ class _RSASignatureContext(object):
def _finalize_pss(self, evp_pkey, pkey_size, evp_md):
data_to_sign = self._hash_ctx.finalize()
- self._hash_ctx = None
padded = self._backend._ffi.new("unsigned char[]", pkey_size)
rsa_cdata = self._backend._lib.EVP_PKEY_get1_RSA(evp_pkey)
assert rsa_cdata != self._backend._ffi.NULL
@@ -1121,7 +1411,7 @@ class _RSAVerificationContext(object):
if not isinstance(padding, interfaces.AsymmetricPadding):
raise TypeError(
- "Expected provider of interfaces.AsymmetricPadding")
+ "Expected provider of interfaces.AsymmetricPadding.")
if isinstance(padding, PKCS1v15):
if self._backend._lib.Cryptography_HAS_PKEY_CTX:
@@ -1132,7 +1422,7 @@ class _RSAVerificationContext(object):
elif isinstance(padding, PSS):
if not isinstance(padding._mgf, MGF1):
raise UnsupportedAlgorithm(
- "Only MGF1 is supported by this backend",
+ "Only MGF1 is supported by this backend.",
_Reasons.UNSUPPORTED_MGF
)
@@ -1159,24 +1449,18 @@ class _RSAVerificationContext(object):
self._verify_method = self._verify_pss
else:
raise UnsupportedAlgorithm(
- "{0} is not supported by this backend".format(padding.name),
+ "{0} is not supported by this backend.".format(padding.name),
_Reasons.UNSUPPORTED_PADDING
)
self._padding = padding
self._algorithm = algorithm
- self._hash_ctx = _HashContext(backend, self._algorithm)
+ self._hash_ctx = hashes.Hash(self._algorithm, self._backend)
def update(self, data):
- if self._hash_ctx is None:
- raise AlreadyFinalized("Context has already been finalized")
-
self._hash_ctx.update(data)
def verify(self):
- if self._hash_ctx is None:
- raise AlreadyFinalized("Context has already been finalized")
-
evp_pkey = self._backend._rsa_public_key_to_evp_pkey(
self._public_key)
@@ -1223,7 +1507,6 @@ class _RSAVerificationContext(object):
assert res > 0
data_to_verify = self._hash_ctx.finalize()
- self._hash_ctx = None
res = self._backend._lib.EVP_PKEY_verify(
pkey_ctx,
self._signature,
@@ -1241,14 +1524,16 @@ class _RSAVerificationContext(object):
raise InvalidSignature
def _verify_pkcs1(self, evp_pkey, evp_md):
+ if self._hash_ctx._ctx is None:
+ raise AlreadyFinalized("Context has already been finalized.")
+
res = self._backend._lib.EVP_VerifyFinal(
- self._hash_ctx._ctx,
+ self._hash_ctx._ctx._ctx,
self._signature,
len(self._signature),
evp_pkey
)
self._hash_ctx.finalize()
- self._hash_ctx = None
# The previous call can return negative numbers in the event of an
# error. This is not a signature failure but we need to fail if it
# occurs.
@@ -1279,7 +1564,6 @@ class _RSAVerificationContext(object):
raise InvalidSignature
data_to_verify = self._hash_ctx.finalize()
- self._hash_ctx = None
res = self._backend._lib.RSA_verify_PKCS1_PSS(
rsa_cdata,
data_to_verify,
@@ -1297,11 +1581,79 @@ class _RSAVerificationContext(object):
raise InvalidSignature
+@utils.register_interface(interfaces.AsymmetricVerificationContext)
+class _DSAVerificationContext(object):
+ def __init__(self, backend, public_key, signature, algorithm):
+ self._backend = backend
+ self._public_key = public_key
+ self._signature = signature
+ self._algorithm = algorithm
+
+ self._hash_ctx = hashes.Hash(self._algorithm, self._backend)
+
+ def update(self, data):
+ self._hash_ctx.update(data)
+
+ def verify(self):
+ self._dsa_cdata = self._backend._dsa_cdata_from_public_key(
+ self._public_key)
+ self._dsa_cdata = self._backend._ffi.gc(self._dsa_cdata,
+ self._backend._lib.DSA_free)
+
+ data_to_verify = self._hash_ctx.finalize()
+
+ # The first parameter passed to DSA_verify is unused by OpenSSL but
+ # must be an integer.
+ res = self._backend._lib.DSA_verify(
+ 0, data_to_verify, len(data_to_verify), self._signature,
+ len(self._signature), self._dsa_cdata)
+
+ if res != 1:
+ errors = self._backend._consume_errors()
+ assert errors
+ if res == -1:
+ assert errors[0].lib == self._backend._lib.ERR_LIB_ASN1
+
+ raise InvalidSignature
+
+
+@utils.register_interface(interfaces.AsymmetricSignatureContext)
+class _DSASignatureContext(object):
+ def __init__(self, backend, private_key, algorithm):
+ self._backend = backend
+ self._private_key = private_key
+ self._algorithm = algorithm
+ self._hash_ctx = hashes.Hash(self._algorithm, self._backend)
+ self._dsa_cdata = self._backend._dsa_cdata_from_private_key(
+ self._private_key)
+ self._dsa_cdata = self._backend._ffi.gc(self._dsa_cdata,
+ self._backend._lib.DSA_free)
+
+ def update(self, data):
+ self._hash_ctx.update(data)
+
+ def finalize(self):
+ data_to_sign = self._hash_ctx.finalize()
+ sig_buf_len = self._backend._lib.DSA_size(self._dsa_cdata)
+ sig_buf = self._backend._ffi.new("unsigned char[]", sig_buf_len)
+ buflen = self._backend._ffi.new("unsigned int *")
+
+ # The first parameter passed to DSA_sign is unused by OpenSSL but
+ # must be an integer.
+ res = self._backend._lib.DSA_sign(
+ 0, data_to_sign, len(data_to_sign), sig_buf,
+ buflen, self._dsa_cdata)
+ assert res == 1
+ assert buflen[0]
+
+ return self._backend._ffi.buffer(sig_buf)[:buflen[0]]
+
+
@utils.register_interface(interfaces.CMACContext)
class _CMACContext(object):
def __init__(self, backend, algorithm, ctx=None):
if not backend.cmac_algorithm_supported(algorithm):
- raise UnsupportedAlgorithm("This backend does not support CMAC",
+ raise UnsupportedAlgorithm("This backend does not support CMAC.",
_Reasons.UNSUPPORTED_CIPHER)
self._backend = backend
diff --git a/cryptography/hazmat/bindings/commoncrypto/binding.py b/cryptography/hazmat/bindings/commoncrypto/binding.py
index 144bb099..ee7378ad 100644
--- a/cryptography/hazmat/bindings/commoncrypto/binding.py
+++ b/cryptography/hazmat/bindings/commoncrypto/binding.py
@@ -25,10 +25,16 @@ class Binding(object):
"""
_module_prefix = "cryptography.hazmat.bindings.commoncrypto."
_modules = [
+ "cf",
"common_digest",
"common_hmac",
"common_key_derivation",
"common_cryptor",
+ "secimport",
+ "secitem",
+ "seckey",
+ "seckeychain",
+ "sectransform",
]
ffi = None
@@ -45,6 +51,7 @@ class Binding(object):
cls.ffi, cls.lib = build_ffi(
module_prefix=cls._module_prefix,
modules=cls._modules,
+ extra_link_args=["-framework", "Security"]
)
@classmethod
diff --git a/cryptography/hazmat/bindings/commoncrypto/cf.py b/cryptography/hazmat/bindings/commoncrypto/cf.py
new file mode 100644
index 00000000..671963a3
--- /dev/null
+++ b/cryptography/hazmat/bindings/commoncrypto/cf.py
@@ -0,0 +1,114 @@
+# 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.
+
+from __future__ import absolute_import, division, print_function
+
+INCLUDES = """
+#include <CoreFoundation/CoreFoundation.h>
+"""
+
+TYPES = """
+typedef bool Boolean;
+typedef signed long OSStatus;
+typedef unsigned char UInt8;
+typedef uint32_t UInt32;
+
+typedef const void * CFAllocatorRef;
+const CFAllocatorRef kCFAllocatorDefault;
+typedef const void * CFDataRef;
+typedef signed long long CFIndex;
+typedef ... *CFStringRef;
+typedef ... *CFArrayRef;
+typedef ... *CFBooleanRef;
+typedef ... *CFErrorRef;
+typedef ... *CFNumberRef;
+typedef ... *CFTypeRef;
+typedef ... *CFDictionaryRef;
+typedef ... *CFMutableDictionaryRef;
+typedef struct {
+ ...;
+} CFDictionaryKeyCallBacks;
+typedef struct {
+ ...;
+} CFDictionaryValueCallBacks;
+typedef struct {
+ ...;
+} CFRange;
+
+typedef UInt32 CFStringEncoding;
+enum {
+ kCFStringEncodingASCII = 0x0600
+};
+
+enum {
+ kCFNumberSInt8Type = 1,
+ kCFNumberSInt16Type = 2,
+ kCFNumberSInt32Type = 3,
+ kCFNumberSInt64Type = 4,
+ kCFNumberFloat32Type = 5,
+ kCFNumberFloat64Type = 6,
+ kCFNumberCharType = 7,
+ kCFNumberShortType = 8,
+ kCFNumberIntType = 9,
+ kCFNumberLongType = 10,
+ kCFNumberLongLongType = 11,
+ kCFNumberFloatType = 12,
+ kCFNumberDoubleType = 13,
+ kCFNumberCFIndexType = 14,
+ kCFNumberNSIntegerType = 15,
+ kCFNumberCGFloatType = 16,
+ kCFNumberMaxType = 16
+};
+typedef int CFNumberType;
+
+const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks;
+const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks;
+
+const CFBooleanRef kCFBooleanTrue;
+const CFBooleanRef kCFBooleanFalse;
+"""
+
+FUNCTIONS = """
+CFDataRef CFDataCreate(CFAllocatorRef, const UInt8 *, CFIndex);
+CFStringRef CFStringCreateWithCString(CFAllocatorRef, const char *,
+ CFStringEncoding);
+CFDictionaryRef CFDictionaryCreate(CFAllocatorRef, const void **,
+ const void **, CFIndex,
+ const CFDictionaryKeyCallBacks *,
+ const CFDictionaryValueCallBacks *);
+CFMutableDictionaryRef CFDictionaryCreateMutable(
+ CFAllocatorRef,
+ CFIndex,
+ const CFDictionaryKeyCallBacks *,
+ const CFDictionaryValueCallBacks *
+);
+void CFDictionarySetValue(CFMutableDictionaryRef, const void *, const void *);
+CFIndex CFArrayGetCount(CFArrayRef);
+const void *CFArrayGetValueAtIndex(CFArrayRef, CFIndex);
+CFIndex CFDataGetLength(CFDataRef);
+void CFDataGetBytes(CFDataRef, CFRange, UInt8 *);
+CFRange CFRangeMake(CFIndex, CFIndex);
+void CFShow(CFTypeRef);
+Boolean CFBooleanGetValue(CFBooleanRef);
+CFNumberRef CFNumberCreate(CFAllocatorRef, CFNumberType, const void *);
+void CFRelease(CFTypeRef);
+CFTypeRef CFRetain(CFTypeRef);
+"""
+
+MACROS = """
+"""
+
+CUSTOMIZATIONS = """
+"""
+
+CONDITIONAL_NAMES = {}
diff --git a/cryptography/hazmat/bindings/commoncrypto/secimport.py b/cryptography/hazmat/bindings/commoncrypto/secimport.py
new file mode 100644
index 00000000..add62c79
--- /dev/null
+++ b/cryptography/hazmat/bindings/commoncrypto/secimport.py
@@ -0,0 +1,95 @@
+# 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.
+
+from __future__ import absolute_import, division, print_function
+
+INCLUDES = """
+#include <Security/SecImportExport.h>
+"""
+
+TYPES = """
+typedef ... *SecAccessRef;
+
+CFStringRef kSecImportExportPassphrase;
+CFStringRef kSecImportExportKeychain;
+CFStringRef kSecImportExportAccess;
+
+typedef uint32_t SecExternalItemType;
+enum {
+ kSecItemTypeUnknown,
+ kSecItemTypePrivateKey,
+ kSecItemTypePublicKey,
+ kSecItemTypeSessionKey,
+ kSecItemTypeCertificate,
+ kSecItemTypeAggregate
+};
+
+
+typedef uint32_t SecExternalFormat;
+enum {
+ kSecFormatUnknown = 0,
+ kSecFormatOpenSSL,
+ kSecFormatSSH,
+ kSecFormatBSAFE,
+ kSecFormatRawKey,
+ kSecFormatWrappedPKCS8,
+ kSecFormatWrappedOpenSSL,
+ kSecFormatWrappedSSH,
+ kSecFormatWrappedLSH,
+ kSecFormatX509Cert,
+ kSecFormatPEMSequence,
+ kSecFormatPKCS7,
+ kSecFormatPKCS12,
+ kSecFormatNetscapeCertSequence,
+ kSecFormatSSHv2
+};
+
+typedef uint32_t SecItemImportExportFlags;
+enum {
+ kSecKeyImportOnlyOne = 0x00000001,
+ kSecKeySecurePassphrase = 0x00000002,
+ kSecKeyNoAccessControl = 0x00000004
+};
+typedef uint32_t SecKeyImportExportFlags;
+
+typedef struct {
+ /* for import and export */
+ uint32_t version;
+ SecKeyImportExportFlags flags;
+ CFTypeRef passphrase;
+ CFStringRef alertTitle;
+ CFStringRef alertPrompt;
+
+ /* for import only */
+ SecAccessRef accessRef;
+ CFArrayRef keyUsage;
+
+ CFArrayRef keyAttributes;
+} SecItemImportExportKeyParameters;
+"""
+
+FUNCTIONS = """
+OSStatus SecItemImport(CFDataRef, CFStringRef, SecExternalFormat *,
+ SecExternalItemType *, SecItemImportExportFlags,
+ const SecItemImportExportKeyParameters *,
+ SecKeychainRef, CFArrayRef *);
+OSStatus SecPKCS12Import(CFDataRef, CFDictionaryRef, CFArrayRef *);
+"""
+
+MACROS = """
+"""
+
+CUSTOMIZATIONS = """
+"""
+
+CONDITIONAL_NAMES = {}
diff --git a/cryptography/hazmat/bindings/commoncrypto/secitem.py b/cryptography/hazmat/bindings/commoncrypto/secitem.py
new file mode 100644
index 00000000..4d7710bd
--- /dev/null
+++ b/cryptography/hazmat/bindings/commoncrypto/secitem.py
@@ -0,0 +1,40 @@
+# 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.
+
+from __future__ import absolute_import, division, print_function
+
+INCLUDES = """
+#include <Security/SecItem.h>
+"""
+
+TYPES = """
+const CFTypeRef kSecAttrKeyType;
+const CFTypeRef kSecAttrKeySizeInBits;
+const CFTypeRef kSecAttrIsPermanent;
+const CFTypeRef kSecAttrKeyTypeRSA;
+const CFTypeRef kSecAttrKeyTypeDSA;
+const CFTypeRef kSecAttrKeyTypeEC;
+const CFTypeRef kSecAttrKeyTypeEC;
+const CFTypeRef kSecUseKeychain;
+"""
+
+FUNCTIONS = """
+"""
+
+MACROS = """
+"""
+
+CUSTOMIZATIONS = """
+"""
+
+CONDITIONAL_NAMES = {}
diff --git a/cryptography/hazmat/bindings/commoncrypto/seckey.py b/cryptography/hazmat/bindings/commoncrypto/seckey.py
new file mode 100644
index 00000000..38aaece8
--- /dev/null
+++ b/cryptography/hazmat/bindings/commoncrypto/seckey.py
@@ -0,0 +1,34 @@
+# 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.
+
+from __future__ import absolute_import, division, print_function
+
+INCLUDES = """
+#include <Security/SecKey.h>
+"""
+
+TYPES = """
+typedef ... *SecKeyRef;
+"""
+
+FUNCTIONS = """
+OSStatus SecKeyGeneratePair(CFDictionaryRef, SecKeyRef *, SecKeyRef *);
+"""
+
+MACROS = """
+"""
+
+CUSTOMIZATIONS = """
+"""
+
+CONDITIONAL_NAMES = {}
diff --git a/cryptography/hazmat/bindings/commoncrypto/seckeychain.py b/cryptography/hazmat/bindings/commoncrypto/seckeychain.py
new file mode 100644
index 00000000..c045c347
--- /dev/null
+++ b/cryptography/hazmat/bindings/commoncrypto/seckeychain.py
@@ -0,0 +1,36 @@
+# 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.
+
+from __future__ import absolute_import, division, print_function
+
+INCLUDES = """
+#include <Security/SecKeychain.h>
+"""
+
+TYPES = """
+typedef ... *SecKeychainRef;
+"""
+
+FUNCTIONS = """
+OSStatus SecKeychainCreate(const char *, UInt32, const void *, Boolean,
+ SecAccessRef, SecKeychainRef *);
+OSStatus SecKeychainDelete(SecKeychainRef);
+"""
+
+MACROS = """
+"""
+
+CUSTOMIZATIONS = """
+"""
+
+CONDITIONAL_NAMES = {}
diff --git a/cryptography/hazmat/bindings/commoncrypto/sectransform.py b/cryptography/hazmat/bindings/commoncrypto/sectransform.py
new file mode 100644
index 00000000..d6dbc5f6
--- /dev/null
+++ b/cryptography/hazmat/bindings/commoncrypto/sectransform.py
@@ -0,0 +1,79 @@
+# 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.
+
+from __future__ import absolute_import, division, print_function
+
+INCLUDES = """
+#include <Security/SecDigestTransform.h>
+#include <Security/SecSignVerifyTransform.h>
+#include <Security/SecEncryptTransform.h>
+"""
+
+TYPES = """
+typedef ... *SecTransformRef;
+
+CFStringRef kSecImportExportPassphrase;
+CFStringRef kSecImportExportKeychain;
+CFStringRef kSecImportExportAccess;
+
+CFStringRef kSecEncryptionMode;
+CFStringRef kSecEncryptKey;
+CFStringRef kSecIVKey;
+CFStringRef kSecModeCBCKey;
+CFStringRef kSecModeCFBKey;
+CFStringRef kSecModeECBKey;
+CFStringRef kSecModeNoneKey;
+CFStringRef kSecModeOFBKey;
+CFStringRef kSecOAEPEncodingParametersAttributeName;
+CFStringRef kSecPaddingKey;
+CFStringRef kSecPaddingNoneKey;
+CFStringRef kSecPaddingOAEPKey;
+CFStringRef kSecPaddingPKCS1Key;
+CFStringRef kSecPaddingPKCS5Key;
+CFStringRef kSecPaddingPKCS7Key;
+
+const CFStringRef kSecTransformInputAttributeName;
+const CFStringRef kSecTransformOutputAttributeName;
+const CFStringRef kSecTransformDebugAttributeName;
+const CFStringRef kSecTransformTransformName;
+const CFStringRef kSecTransformAbortAttributeName;
+
+CFStringRef kSecInputIsAttributeName;
+CFStringRef kSecInputIsPlainText;
+CFStringRef kSecInputIsDigest;
+CFStringRef kSecInputIsRaw;
+
+const CFStringRef kSecDigestTypeAttribute;
+const CFStringRef kSecDigestLengthAttribute;
+const CFStringRef kSecDigestMD5;
+const CFStringRef kSecDigestSHA1;
+const CFStringRef kSecDigestSHA2;
+"""
+
+FUNCTIONS = """
+Boolean SecTransformSetAttribute(SecTransformRef, CFStringRef, CFTypeRef,
+ CFErrorRef *);
+SecTransformRef SecDecryptTransformCreate(SecKeyRef, CFErrorRef *);
+SecTransformRef SecEncryptTransformCreate(SecKeyRef, CFErrorRef *);
+SecTransformRef SecVerifyTransformCreate(SecKeyRef, CFDataRef, CFErrorRef *);
+SecTransformRef SecSignTransformCreate(SecKeyRef, CFErrorRef *) ;
+CFTypeRef SecTransformExecute(SecTransformRef, CFErrorRef *);
+"""
+
+MACROS = """
+"""
+
+CUSTOMIZATIONS = """
+"""
+
+CONDITIONAL_NAMES = {}
diff --git a/cryptography/hazmat/bindings/openssl/aes.py b/cryptography/hazmat/bindings/openssl/aes.py
index 17c154cf..b0e00721 100644
--- a/cryptography/hazmat/bindings/openssl/aes.py
+++ b/cryptography/hazmat/bindings/openssl/aes.py
@@ -29,6 +29,12 @@ typedef struct aes_key_st AES_KEY;
FUNCTIONS = """
int AES_set_encrypt_key(const unsigned char *, const int, AES_KEY *);
int AES_set_decrypt_key(const unsigned char *, const int, AES_KEY *);
+/* The ctr128_encrypt function is only useful in 0.9.8. You should use EVP for
+ this in 1.0.0+. */
+void AES_ctr128_encrypt(const unsigned char *, unsigned char *,
+ const unsigned long, const AES_KEY *,
+ unsigned char[], unsigned char[], unsigned int *);
+
"""
MACROS = """
diff --git a/cryptography/hazmat/bindings/openssl/asn1.py b/cryptography/hazmat/bindings/openssl/asn1.py
index dfdf1bf5..2edfd2d8 100644
--- a/cryptography/hazmat/bindings/openssl/asn1.py
+++ b/cryptography/hazmat/bindings/openssl/asn1.py
@@ -141,6 +141,9 @@ ASN1_INTEGER *BN_to_ASN1_INTEGER(BIGNUM *, ASN1_INTEGER *);
/* These isn't a macro the arg is const on openssl 1.0.2+ */
int ASN1_GENERALIZEDTIME_check(ASN1_GENERALIZEDTIME *);
+
+/* Not a macro, const on openssl 1.0 */
+int ASN1_STRING_set_default_mask_asc(char *);
"""
CUSTOMIZATIONS = """
diff --git a/cryptography/hazmat/bindings/openssl/binding.py b/cryptography/hazmat/bindings/openssl/binding.py
index f0ff3275..464081b0 100644
--- a/cryptography/hazmat/bindings/openssl/binding.py
+++ b/cryptography/hazmat/bindings/openssl/binding.py
@@ -55,6 +55,7 @@ class Binding(object):
"dh",
"dsa",
"ec",
+ "ecdh",
"ecdsa",
"engine",
"err",
@@ -148,7 +149,7 @@ class Binding(object):
lock.release()
else:
raise RuntimeError(
- "Unknown lock mode {0}: lock={1}, file={2}, line={3}".format(
+ "Unknown lock mode {0}: lock={1}, file={2}, line={3}.".format(
mode, n, file, line
)
)
diff --git a/cryptography/hazmat/bindings/openssl/bio.py b/cryptography/hazmat/bindings/openssl/bio.py
index 0c521b4d..cfe6034f 100644
--- a/cryptography/hazmat/bindings/openssl/bio.py
+++ b/cryptography/hazmat/bindings/openssl/bio.py
@@ -123,10 +123,10 @@ long BIO_callback_ctrl(
int,
void (*)(struct bio_st *, int, const char *, int, long, long)
);
-char* BIO_ptr_ctrl(BIO *bp, int cmd, long larg);
-long BIO_int_ctrl(BIO *bp, int cmd, long larg, int iarg);
-size_t BIO_ctrl_pending(BIO *b);
-size_t BIO_ctrl_wpending(BIO *b);
+char *BIO_ptr_ctrl(BIO *, int, long);
+long BIO_int_ctrl(BIO *, int, long, int);
+size_t BIO_ctrl_pending(BIO *);
+size_t BIO_ctrl_wpending(BIO *);
int BIO_read(BIO *, void *, int);
int BIO_gets(BIO *, char *, int);
int BIO_write(BIO *, const void *, int);
diff --git a/cryptography/hazmat/bindings/openssl/dh.py b/cryptography/hazmat/bindings/openssl/dh.py
index 1791a670..a0f99479 100644
--- a/cryptography/hazmat/bindings/openssl/dh.py
+++ b/cryptography/hazmat/bindings/openssl/dh.py
@@ -34,9 +34,21 @@ typedef struct dh_st {
FUNCTIONS = """
DH *DH_new(void);
void DH_free(DH *);
+int DH_size(const DH *);
+DH *DH_generate_parameters(int, int, void (*)(int, int, void *), void *);
+int DH_check(const DH *, int *);
+int DH_generate_key(DH *);
+int DH_compute_key(unsigned char *, const BIGNUM *, DH *);
+int DH_set_ex_data(DH *, int, void *);
+void *DH_get_ex_data(DH *, int);
+DH *d2i_DHparams(DH **, const unsigned char **, long);
+int i2d_DHparams(const DH *, unsigned char **);
+int DHparams_print_fp(FILE *, const DH *);
+int DHparams_print(BIO *, const DH *);
"""
MACROS = """
+int DH_generate_parameters_ex(DH *, int, int, BN_GENCB *);
"""
CUSTOMIZATIONS = """
diff --git a/cryptography/hazmat/bindings/openssl/ec.py b/cryptography/hazmat/bindings/openssl/ec.py
index 45c17c2e..26fc8ff0 100644
--- a/cryptography/hazmat/bindings/openssl/ec.py
+++ b/cryptography/hazmat/bindings/openssl/ec.py
@@ -27,6 +27,8 @@ static const int Cryptography_HAS_EC_1_0_1;
static const int Cryptography_HAS_EC_NISTP_64_GCC_128;
static const int Cryptography_HAS_EC2M;
+static const int OPENSSL_EC_NAMED_CURVE;
+
typedef ... EC_KEY;
typedef ... EC_GROUP;
typedef ... EC_POINT;
@@ -61,6 +63,8 @@ int EC_GROUP_set_curve_GF2m(
int EC_GROUP_get_curve_GF2m(
const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *);
+int EC_GROUP_get_degree(const EC_GROUP *);
+
const EC_METHOD *EC_GROUP_method_of(const EC_GROUP *);
const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *);
int EC_GROUP_get_curve_name(const EC_GROUP *);
@@ -198,6 +202,7 @@ int EC_METHOD_get_field_type(const EC_METHOD *);
CUSTOMIZATIONS = """
#ifdef OPENSSL_NO_EC
static const long Cryptography_HAS_EC = 0;
+
typedef void EC_KEY;
typedef void EC_GROUP;
typedef void EC_POINT;
@@ -208,6 +213,8 @@ typedef struct {
} EC_builtin_curve;
typedef long point_conversion_form_t;
+static const int OPENSSL_EC_NAMED_CURVE = 0;
+
void (*EC_KEY_free)(EC_KEY *) = NULL;
size_t (*EC_get_builtin_curves)(EC_builtin_curve *, size_t) = NULL;
EC_KEY *(*EC_KEY_new_by_curve_name)(int) = NULL;
@@ -250,6 +257,8 @@ int (*EC_GROUP_set_curve_GFp)(
int (*EC_GROUP_get_curve_GFp)(
const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *);
+int (*EC_GROUP_get_degree)(const EC_GROUP *) = NULL;
+
const EC_METHOD *(*EC_GROUP_method_of)(const EC_GROUP *) = NULL;
const EC_POINT *(*EC_GROUP_get0_generator)(const EC_GROUP *) = NULL;
int (*EC_GROUP_get_curve_name)(const EC_GROUP *) = NULL;
@@ -389,6 +398,7 @@ static const long Cryptography_HAS_EC2M = 1;
CONDITIONAL_NAMES = {
"Cryptography_HAS_EC": [
+ "OPENSSL_EC_NAMED_CURVE",
"EC_GROUP_new",
"EC_GROUP_free",
"EC_GROUP_clear_free",
@@ -399,6 +409,7 @@ CONDITIONAL_NAMES = {
"EC_GROUP_method_of",
"EC_GROUP_get0_generator",
"EC_GROUP_get_curve_name",
+ "EC_GROUP_get_degree",
"EC_KEY_free",
"EC_get_builtin_curves",
"EC_KEY_new_by_curve_name",
diff --git a/cryptography/hazmat/bindings/openssl/ecdh.py b/cryptography/hazmat/bindings/openssl/ecdh.py
new file mode 100644
index 00000000..960d46fb
--- /dev/null
+++ b/cryptography/hazmat/bindings/openssl/ecdh.py
@@ -0,0 +1,68 @@
+# 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.
+
+from __future__ import absolute_import, division, print_function
+
+INCLUDES = """
+#ifndef OPENSSL_NO_ECDH
+#include <openssl/ecdh.h>
+#endif
+"""
+
+TYPES = """
+static const int Cryptography_HAS_ECDH;
+"""
+
+FUNCTIONS = """
+"""
+
+MACROS = """
+int ECDH_compute_key(void *, size_t, const EC_POINT *, EC_KEY *,
+ void *(*)(const void *, size_t, void *, size_t *));
+
+int ECDH_get_ex_new_index(long, void *, CRYPTO_EX_new *, CRYPTO_EX_dup *,
+ CRYPTO_EX_free *);
+
+int ECDH_set_ex_data(EC_KEY *, int, void *);
+
+void *ECDH_get_ex_data(EC_KEY *, int);
+"""
+
+CUSTOMIZATIONS = """
+#ifdef OPENSSL_NO_ECDH
+static const long Cryptography_HAS_ECDH = 0;
+
+int (*ECDH_compute_key)(void *, size_t, const EC_POINT *, EC_KEY *,
+ void *(*)(const void *, size_t, void *,
+ size_t *)) = NULL;
+
+int (*ECDH_get_ex_new_index)(long, void *, CRYPTO_EX_new *, CRYPTO_EX_dup *,
+ CRYPTO_EX_free *) = NULL;
+
+int (*ECDH_set_ex_data)(EC_KEY *, int, void *) = NULL;
+
+void *(*ECDH_get_ex_data)(EC_KEY *, int) = NULL;
+
+#else
+static const long Cryptography_HAS_ECDH = 1;
+#endif
+"""
+
+CONDITIONAL_NAMES = {
+ "Cryptography_HAS_ECDH": [
+ "ECDH_compute_key",
+ "ECDH_get_ex_new_index",
+ "ECDH_set_ex_data",
+ "ECDH_get_ex_data",
+ ],
+}
diff --git a/cryptography/hazmat/bindings/openssl/err.py b/cryptography/hazmat/bindings/openssl/err.py
index f6456d66..f685e494 100644
--- a/cryptography/hazmat/bindings/openssl/err.py
+++ b/cryptography/hazmat/bindings/openssl/err.py
@@ -135,6 +135,7 @@ static const int EVP_F_PKCS5_V2_PBE_KEYIVGEN;
static const int EVP_F_PKCS8_SET_BROKEN;
static const int EVP_F_RC2_MAGIC_TO_METH;
static const int EVP_F_RC5_CTRL;
+
static const int EVP_R_AES_KEY_SETUP_FAILED;
static const int EVP_R_ASN1_LIB;
static const int EVP_R_BAD_BLOCK_LENGTH;
@@ -168,6 +169,7 @@ static const int EVP_R_UNSUPPORTED_CIPHER;
static const int EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION;
static const int EVP_R_UNSUPPORTED_KEYLENGTH;
static const int EVP_R_UNSUPPORTED_SALT_TYPE;
+static const int EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM;
static const int EVP_R_WRONG_FINAL_BLOCK_LENGTH;
static const int EVP_R_WRONG_PUBLIC_KEY_TYPE;
diff --git a/cryptography/hazmat/bindings/openssl/opensslv.py b/cryptography/hazmat/bindings/openssl/opensslv.py
index e4aa6212..ef6e057b 100644
--- a/cryptography/hazmat/bindings/openssl/opensslv.py
+++ b/cryptography/hazmat/bindings/openssl/opensslv.py
@@ -18,6 +18,8 @@ INCLUDES = """
"""
TYPES = """
+/* Note that these will be resolved when cryptography is compiled and are NOT
+ guaranteed to be the version that it actually loads. */
static const int OPENSSL_VERSION_NUMBER;
static const char *const OPENSSL_VERSION_TEXT;
"""
diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py
index 7ed42f9f..94b96d98 100644
--- a/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/cryptography/hazmat/bindings/openssl/ssl.py
@@ -15,6 +15,8 @@ from __future__ import absolute_import, division, print_function
INCLUDES = """
#include <openssl/ssl.h>
+
+typedef STACK_OF(SSL_CIPHER) Cryptography_STACK_OF_SSL_CIPHER;
"""
TYPES = """
@@ -24,6 +26,7 @@ TYPES = """
static const long Cryptography_HAS_SSL2;
static const long Cryptography_HAS_TLSv1_1;
static const long Cryptography_HAS_TLSv1_2;
+static const long Cryptography_HAS_SECURE_RENEGOTIATION;
/* Internally invented symbol to tell us if SNI is supported */
static const long Cryptography_HAS_TLSEXT_HOSTNAME;
@@ -84,6 +87,8 @@ static const long SSL_OP_COOKIE_EXCHANGE;
static const long SSL_OP_NO_TICKET;
static const long SSL_OP_ALL;
static const long SSL_OP_SINGLE_ECDH_USE;
+static const long SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+static const long SSL_OP_LEGACY_SERVER_CONNECT;
static const long SSL_VERIFY_PEER;
static const long SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
static const long SSL_VERIFY_CLIENT_ONCE;
@@ -153,6 +158,8 @@ typedef struct {
static const long TLSEXT_NAMETYPE_host_name;
typedef ... SSL_CIPHER;
+typedef ... Cryptography_STACK_OF_SSL_CIPHER;
+typedef ... COMP_METHOD;
"""
FUNCTIONS = """
@@ -160,6 +167,7 @@ void SSL_load_error_strings(void);
int SSL_library_init(void);
/* SSL */
+const char *SSL_state_string_long(const SSL *);
SSL_SESSION *SSL_get1_session(SSL *);
int SSL_set_session(SSL *, SSL_SESSION *);
int SSL_get_verify_mode(const SSL *);
@@ -189,6 +197,11 @@ int SSL_get_error(const SSL *, int);
int SSL_do_handshake(SSL *);
int SSL_shutdown(SSL *);
const char *SSL_get_cipher_list(const SSL *, int);
+Cryptography_STACK_OF_SSL_CIPHER *SSL_get_ciphers(const SSL *);
+
+const COMP_METHOD *SSL_get_current_compression(SSL *);
+const COMP_METHOD *SSL_get_current_expansion(SSL *);
+const char *SSL_COMP_get_name(const COMP_METHOD *);
/* context */
void SSL_CTX_free(SSL_CTX *);
@@ -247,6 +260,7 @@ int SSL_want_read(const SSL *);
int SSL_want_write(const SSL *);
long SSL_total_renegotiations(SSL *);
+long SSL_get_secure_renegotiation_support(SSL *);
/* Defined as unsigned long because SSL_OP_ALL is greater than signed 32-bit
and Windows defines long as 32-bit. */
@@ -350,9 +364,23 @@ int SSL_select_next_proto(unsigned char **, unsigned char *,
const unsigned char *, unsigned int);
void SSL_get0_next_proto_negotiated(const SSL *,
const unsigned char **, unsigned *);
+
+int sk_SSL_CIPHER_num(Cryptography_STACK_OF_SSL_CIPHER *);
+SSL_CIPHER *sk_SSL_CIPHER_value(Cryptography_STACK_OF_SSL_CIPHER *, int);
"""
CUSTOMIZATIONS = """
+/** Secure renegotiation is supported in OpenSSL >= 0.9.8m
+ * But some Linux distributions have back ported some features.
+ */
+#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+static const long Cryptography_HAS_SECURE_RENEGOTIATION = 0;
+long (*SSL_get_secure_renegotiation_support)(SSL *) = NULL;
+const long SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0;
+const long SSL_OP_LEGACY_SERVER_CONNECT = 0;
+#else
+static const long Cryptography_HAS_SECURE_RENEGOTIATION = 1;
+#endif
#ifdef OPENSSL_NO_SSL2
static const long Cryptography_HAS_SSL2 = 0;
SSL_METHOD* (*SSLv2_method)(void) = NULL;
@@ -550,5 +578,11 @@ CONDITIONAL_NAMES = {
"SSL_CTX_set_next_proto_select_cb",
"SSL_select_next_proto",
"SSL_get0_next_proto_negotiated",
- ]
+ ],
+
+ "Cryptography_HAS_SECURE_RENEGOTIATION": [
+ "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION",
+ "SSL_OP_LEGACY_SERVER_CONNECT",
+ "SSL_get_secure_renegotiation_support",
+ ],
}
diff --git a/cryptography/hazmat/primitives/asymmetric/dsa.py b/cryptography/hazmat/primitives/asymmetric/dsa.py
index 4c2de36a..a9ae9ecb 100644
--- a/cryptography/hazmat/primitives/asymmetric/dsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/dsa.py
@@ -27,7 +27,7 @@ def _check_dsa_parameters(modulus, subgroup_order, generator):
not isinstance(subgroup_order, six.integer_types) or
not isinstance(generator, six.integer_types)
):
- raise TypeError("DSA parameters must be integers")
+ raise TypeError("DSA parameters must be integers.")
if (utils.bit_length(modulus),
utils.bit_length(subgroup_order)) not in (
@@ -36,10 +36,10 @@ def _check_dsa_parameters(modulus, subgroup_order, generator):
(3072, 256)):
raise ValueError("modulus and subgroup_order lengths must be "
"one of these pairs (1024, 160) or (2048, 256) "
- "or (3072, 256)")
+ "or (3072, 256).")
if generator <= 1 or generator >= modulus:
- raise ValueError("generator must be > 1 and < modulus")
+ raise ValueError("generator must be > 1 and < modulus.")
@utils.register_interface(interfaces.DSAParameters)
@@ -55,7 +55,7 @@ class DSAParameters(object):
def generate(cls, key_size, backend):
if not isinstance(backend, DSABackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement DSABackend",
+ "Backend object does not implement DSABackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
@@ -94,13 +94,13 @@ class DSAPrivateKey(object):
not isinstance(x, six.integer_types) or
not isinstance(y, six.integer_types)
):
- raise TypeError("DSAPrivateKey arguments must be integers")
+ raise TypeError("DSAPrivateKey arguments must be integers.")
if x <= 0 or x >= subgroup_order:
- raise ValueError("x must be > 0 and < subgroup_order")
+ raise ValueError("x must be > 0 and < subgroup_order.")
if y != pow(generator, x, modulus):
- raise ValueError("y must be equal to (generator ** x % modulus)")
+ raise ValueError("y must be equal to (generator ** x % modulus).")
self._modulus = modulus
self._subgroup_order = subgroup_order
@@ -112,12 +112,21 @@ class DSAPrivateKey(object):
def generate(cls, parameters, backend):
if not isinstance(backend, DSABackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement DSABackend",
+ "Backend object does not implement DSABackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
return backend.generate_dsa_private_key(parameters)
+ def signer(self, algorithm, backend):
+ if not isinstance(backend, DSABackend):
+ raise UnsupportedAlgorithm(
+ "Backend object does not implement DSABackend.",
+ _Reasons.BACKEND_MISSING_INTERFACE
+ )
+
+ return backend.create_dsa_signature_ctx(self, algorithm)
+
@property
def key_size(self):
return utils.bit_length(self._modulus)
@@ -144,13 +153,23 @@ class DSAPublicKey(object):
def __init__(self, modulus, subgroup_order, generator, y):
_check_dsa_parameters(modulus, subgroup_order, generator)
if not isinstance(y, six.integer_types):
- raise TypeError("y must be an integer")
+ raise TypeError("y must be an integer.")
self._modulus = modulus
self._subgroup_order = subgroup_order
self._generator = generator
self._y = y
+ def verifier(self, signature, algorithm, backend):
+ if not isinstance(backend, DSABackend):
+ raise UnsupportedAlgorithm(
+ "Backend object does not implement DSABackend.",
+ _Reasons.BACKEND_MISSING_INTERFACE
+ )
+
+ return backend.create_dsa_verification_ctx(self, signature,
+ algorithm)
+
@property
def key_size(self):
return utils.bit_length(self._modulus)
diff --git a/cryptography/hazmat/primitives/asymmetric/ec.py b/cryptography/hazmat/primitives/asymmetric/ec.py
new file mode 100644
index 00000000..1e49ad7b
--- /dev/null
+++ b/cryptography/hazmat/primitives/asymmetric/ec.py
@@ -0,0 +1,69 @@
+# 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.
+
+from __future__ import absolute_import, division, print_function
+
+import six
+
+from cryptography.hazmat.primitives import interfaces
+
+
+class EllipticCurvePublicNumbers(object):
+ def __init__(self, x, y, curve):
+ if (
+ not isinstance(x, six.integer_types) or
+ not isinstance(y, six.integer_types)
+ ):
+ raise TypeError("x and y must be integers.")
+
+ if not isinstance(curve, interfaces.EllipticCurve):
+ raise TypeError("curve must provide the EllipticCurve interface.")
+
+ self._y = y
+ self._x = x
+ self._curve = curve
+
+ @property
+ def curve(self):
+ return self._curve
+
+ @property
+ def x(self):
+ return self._x
+
+ @property
+ def y(self):
+ return self._y
+
+
+class EllipticCurvePrivateNumbers(object):
+ def __init__(self, private_value, public_numbers):
+ if not isinstance(private_value, six.integer_types):
+ raise TypeError("private_value must be an integer.")
+
+ if not isinstance(public_numbers, EllipticCurvePublicNumbers):
+ raise TypeError(
+ "public_numbers must be an EllipticCurvePublicNumbers "
+ "instance."
+ )
+
+ self._private_value = private_value
+ self._public_numbers = public_numbers
+
+ @property
+ def private_value(self):
+ return self._private_value
+
+ @property
+ def public_numbers(self):
+ return self._public_numbers
diff --git a/cryptography/hazmat/primitives/asymmetric/padding.py b/cryptography/hazmat/primitives/asymmetric/padding.py
index dcc6fe06..d44bbda5 100644
--- a/cryptography/hazmat/primitives/asymmetric/padding.py
+++ b/cryptography/hazmat/primitives/asymmetric/padding.py
@@ -38,18 +38,19 @@ class PSS(object):
warnings.warn(
"salt_length is deprecated on MGF1 and should be added via the"
" PSS constructor.",
- utils.DeprecatedIn04
+ utils.DeprecatedIn04,
+ stacklevel=2
)
else:
if (not isinstance(salt_length, six.integer_types) and
salt_length is not self.MAX_LENGTH):
- raise TypeError("salt_length must be an integer")
+ raise TypeError("salt_length must be an integer.")
if salt_length is not self.MAX_LENGTH and salt_length < 0:
- raise ValueError("salt_length must be zero or greater")
+ raise ValueError("salt_length must be zero or greater.")
if salt_length is None and self._mgf._salt_length is None:
- raise ValueError("You must supply salt_length")
+ raise ValueError("You must supply salt_length.")
self._salt_length = salt_length
@@ -80,13 +81,14 @@ class MGF1(object):
warnings.warn(
"salt_length is deprecated on MGF1 and should be passed to "
"the PSS constructor instead.",
- utils.DeprecatedIn04
+ utils.DeprecatedIn04,
+ stacklevel=2
)
if (not isinstance(salt_length, six.integer_types) and
salt_length is not self.MAX_LENGTH):
- raise TypeError("salt_length must be an integer")
+ raise TypeError("salt_length must be an integer.")
if salt_length is not self.MAX_LENGTH and salt_length < 0:
- raise ValueError("salt_length must be zero or greater")
+ raise ValueError("salt_length must be zero or greater.")
self._salt_length = salt_length
diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py
index d23f8046..481797fe 100644
--- a/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -21,6 +21,17 @@ from cryptography.hazmat.backends.interfaces import RSABackend
from cryptography.hazmat.primitives import interfaces
+def _verify_rsa_parameters(public_exponent, key_size):
+ if public_exponent < 3:
+ raise ValueError("public_exponent must be >= 3.")
+
+ if public_exponent & 1 == 0:
+ raise ValueError("public_exponent must be odd.")
+
+ if key_size < 512:
+ raise ValueError("key_size must be at least 512-bits.")
+
+
@utils.register_interface(interfaces.RSAPublicKey)
class RSAPublicKey(object):
def __init__(self, public_exponent, modulus):
@@ -28,16 +39,16 @@ class RSAPublicKey(object):
not isinstance(public_exponent, six.integer_types) or
not isinstance(modulus, six.integer_types)
):
- raise TypeError("RSAPublicKey arguments must be integers")
+ raise TypeError("RSAPublicKey arguments must be integers.")
if modulus < 3:
- raise ValueError("modulus must be >= 3")
+ raise ValueError("modulus must be >= 3.")
if public_exponent < 3 or public_exponent >= modulus:
- raise ValueError("public_exponent must be >= 3 and < modulus")
+ raise ValueError("public_exponent must be >= 3 and < modulus.")
if public_exponent & 1 == 0:
- raise ValueError("public_exponent must be odd")
+ raise ValueError("public_exponent must be odd.")
self._public_exponent = public_exponent
self._modulus = modulus
@@ -45,7 +56,7 @@ class RSAPublicKey(object):
def verifier(self, signature, padding, algorithm, backend):
if not isinstance(backend, RSABackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement RSABackend",
+ "Backend object does not implement RSABackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
@@ -55,7 +66,7 @@ class RSAPublicKey(object):
def encrypt(self, plaintext, padding, backend):
if not isinstance(backend, RSABackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement RSABackend",
+ "Backend object does not implement RSABackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
@@ -132,43 +143,43 @@ class RSAPrivateKey(object):
not isinstance(public_exponent, six.integer_types) or
not isinstance(modulus, six.integer_types)
):
- raise TypeError("RSAPrivateKey arguments must be integers")
+ raise TypeError("RSAPrivateKey arguments must be integers.")
if modulus < 3:
- raise ValueError("modulus must be >= 3")
+ raise ValueError("modulus must be >= 3.")
if p >= modulus:
- raise ValueError("p must be < modulus")
+ raise ValueError("p must be < modulus.")
if q >= modulus:
- raise ValueError("q must be < modulus")
+ raise ValueError("q must be < modulus.")
if dmp1 >= modulus:
- raise ValueError("dmp1 must be < modulus")
+ raise ValueError("dmp1 must be < modulus.")
if dmq1 >= modulus:
- raise ValueError("dmq1 must be < modulus")
+ raise ValueError("dmq1 must be < modulus.")
if iqmp >= modulus:
- raise ValueError("iqmp must be < modulus")
+ raise ValueError("iqmp must be < modulus.")
if private_exponent >= modulus:
- raise ValueError("private_exponent must be < modulus")
+ raise ValueError("private_exponent must be < modulus.")
if public_exponent < 3 or public_exponent >= modulus:
- raise ValueError("public_exponent must be >= 3 and < modulus")
+ raise ValueError("public_exponent must be >= 3 and < modulus.")
if public_exponent & 1 == 0:
- raise ValueError("public_exponent must be odd")
+ raise ValueError("public_exponent must be odd.")
if dmp1 & 1 == 0:
- raise ValueError("dmp1 must be odd")
+ raise ValueError("dmp1 must be odd.")
if dmq1 & 1 == 0:
- raise ValueError("dmq1 must be odd")
+ raise ValueError("dmq1 must be odd.")
if p * q != modulus:
- raise ValueError("p*q must equal modulus")
+ raise ValueError("p*q must equal modulus.")
self._p = p
self._q = q
@@ -183,16 +194,17 @@ class RSAPrivateKey(object):
def generate(cls, public_exponent, key_size, backend):
if not isinstance(backend, RSABackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement RSABackend",
+ "Backend object does not implement RSABackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
+ _verify_rsa_parameters(public_exponent, key_size)
return backend.generate_rsa_private_key(public_exponent, key_size)
def signer(self, padding, algorithm, backend):
if not isinstance(backend, RSABackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement RSABackend",
+ "Backend object does not implement RSABackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
@@ -201,7 +213,7 @@ class RSAPrivateKey(object):
def decrypt(self, ciphertext, padding, backend):
if not isinstance(backend, RSABackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement RSABackend",
+ "Backend object does not implement RSABackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
diff --git a/cryptography/hazmat/primitives/ciphers/algorithms.py b/cryptography/hazmat/primitives/ciphers/algorithms.py
index 52daf178..bd8437c2 100644
--- a/cryptography/hazmat/primitives/ciphers/algorithms.py
+++ b/cryptography/hazmat/primitives/ciphers/algorithms.py
@@ -20,7 +20,7 @@ from cryptography.hazmat.primitives import interfaces
def _verify_key_size(algorithm, key):
# Verify that the key size matches the expected key size
if len(key) * 8 not in algorithm.key_sizes:
- raise ValueError("Invalid key size ({0}) for {1}".format(
+ raise ValueError("Invalid key size ({0}) for {1}.".format(
len(key) * 8, algorithm.name
))
return key
diff --git a/cryptography/hazmat/primitives/ciphers/base.py b/cryptography/hazmat/primitives/ciphers/base.py
index 2274e945..e3fe5adc 100644
--- a/cryptography/hazmat/primitives/ciphers/base.py
+++ b/cryptography/hazmat/primitives/ciphers/base.py
@@ -26,12 +26,14 @@ class Cipher(object):
def __init__(self, algorithm, mode, backend):
if not isinstance(backend, CipherBackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement CipherBackend",
+ "Backend object does not implement CipherBackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
if not isinstance(algorithm, interfaces.CipherAlgorithm):
- raise TypeError("Expected interface of interfaces.CipherAlgorithm")
+ raise TypeError(
+ "Expected interface of interfaces.CipherAlgorithm."
+ )
if mode is not None:
mode.validate_for_algorithm(algorithm)
@@ -44,7 +46,7 @@ class Cipher(object):
if isinstance(self.mode, interfaces.ModeWithAuthenticationTag):
if self.mode.tag is not None:
raise ValueError(
- "Authentication tag must be None when encrypting"
+ "Authentication tag must be None when encrypting."
)
ctx = self._backend.create_symmetric_encryption_ctx(
self.algorithm, self.mode
@@ -55,7 +57,7 @@ class Cipher(object):
if isinstance(self.mode, interfaces.ModeWithAuthenticationTag):
if self.mode.tag is None:
raise ValueError(
- "Authentication tag must be provided when decrypting"
+ "Authentication tag must be provided when decrypting."
)
ctx = self._backend.create_symmetric_decryption_ctx(
self.algorithm, self.mode
@@ -79,12 +81,12 @@ class _CipherContext(object):
def update(self, data):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
return self._ctx.update(data)
def finalize(self):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
data = self._ctx.finalize()
self._ctx = None
return data
@@ -100,13 +102,13 @@ class _AEADCipherContext(object):
def update(self, data):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
self._updated = True
return self._ctx.update(data)
def finalize(self):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
data = self._ctx.finalize()
self._tag = self._ctx.tag
self._ctx = None
@@ -114,9 +116,9 @@ class _AEADCipherContext(object):
def authenticate_additional_data(self, data):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
if self._updated:
- raise AlreadyUpdated("Update has been called on this context")
+ raise AlreadyUpdated("Update has been called on this context.")
self._ctx.authenticate_additional_data(data)
@@ -126,5 +128,5 @@ class _AEADEncryptionContext(_AEADCipherContext):
def tag(self):
if self._ctx is not None:
raise NotYetFinalized("You must finalize encryption before "
- "getting the tag")
+ "getting the tag.")
return self._tag
diff --git a/cryptography/hazmat/primitives/ciphers/modes.py b/cryptography/hazmat/primitives/ciphers/modes.py
index 739f23dd..e70a9db5 100644
--- a/cryptography/hazmat/primitives/ciphers/modes.py
+++ b/cryptography/hazmat/primitives/ciphers/modes.py
@@ -17,6 +17,13 @@ from cryptography import utils
from cryptography.hazmat.primitives import interfaces
+def _check_iv_length(mode, algorithm):
+ if len(mode.initialization_vector) * 8 != algorithm.block_size:
+ raise ValueError("Invalid IV size ({0}) for {1}.".format(
+ len(mode.initialization_vector), mode.name
+ ))
+
+
@utils.register_interface(interfaces.Mode)
@utils.register_interface(interfaces.ModeWithInitializationVector)
class CBC(object):
@@ -25,11 +32,7 @@ class CBC(object):
def __init__(self, initialization_vector):
self.initialization_vector = initialization_vector
- def validate_for_algorithm(self, algorithm):
- if len(self.initialization_vector) * 8 != algorithm.block_size:
- raise ValueError("Invalid iv size ({0}) for {1}".format(
- len(self.initialization_vector), self.name
- ))
+ validate_for_algorithm = _check_iv_length
@utils.register_interface(interfaces.Mode)
@@ -48,11 +51,7 @@ class OFB(object):
def __init__(self, initialization_vector):
self.initialization_vector = initialization_vector
- def validate_for_algorithm(self, algorithm):
- if len(self.initialization_vector) * 8 != algorithm.block_size:
- raise ValueError("Invalid iv size ({0}) for {1}".format(
- len(self.initialization_vector), self.name
- ))
+ validate_for_algorithm = _check_iv_length
@utils.register_interface(interfaces.Mode)
@@ -63,11 +62,18 @@ class CFB(object):
def __init__(self, initialization_vector):
self.initialization_vector = initialization_vector
- def validate_for_algorithm(self, algorithm):
- if len(self.initialization_vector) * 8 != algorithm.block_size:
- raise ValueError("Invalid iv size ({0}) for {1}".format(
- len(self.initialization_vector), self.name
- ))
+ validate_for_algorithm = _check_iv_length
+
+
+@utils.register_interface(interfaces.Mode)
+@utils.register_interface(interfaces.ModeWithInitializationVector)
+class CFB8(object):
+ name = "CFB8"
+
+ def __init__(self, initialization_vector):
+ self.initialization_vector = initialization_vector
+
+ validate_for_algorithm = _check_iv_length
@utils.register_interface(interfaces.Mode)
@@ -80,7 +86,7 @@ class CTR(object):
def validate_for_algorithm(self, algorithm):
if len(self.nonce) * 8 != algorithm.block_size:
- raise ValueError("Invalid nonce size ({0}) for {1}".format(
+ raise ValueError("Invalid nonce size ({0}) for {1}.".format(
len(self.nonce), self.name
))
@@ -97,7 +103,7 @@ class GCM(object):
# for it
if tag is not None and len(tag) < 4:
raise ValueError(
- "Authentication tag must be 4 bytes or longer"
+ "Authentication tag must be 4 bytes or longer."
)
self.initialization_vector = initialization_vector
diff --git a/cryptography/hazmat/primitives/cmac.py b/cryptography/hazmat/primitives/cmac.py
index 7e7f65ab..fa463ae0 100644
--- a/cryptography/hazmat/primitives/cmac.py
+++ b/cryptography/hazmat/primitives/cmac.py
@@ -13,8 +13,6 @@
from __future__ import absolute_import, division, print_function
-import six
-
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons
@@ -28,13 +26,13 @@ class CMAC(object):
def __init__(self, algorithm, backend, ctx=None):
if not isinstance(backend, CMACBackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement CMACBackend",
+ "Backend object does not implement CMACBackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
if not isinstance(algorithm, interfaces.BlockCipherAlgorithm):
raise TypeError(
- "Expected instance of interfaces.BlockCipherAlgorithm"
+ "Expected instance of interfaces.BlockCipherAlgorithm."
)
self._algorithm = algorithm
@@ -46,28 +44,28 @@ class CMAC(object):
def update(self, data):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
- if isinstance(data, six.text_type):
- raise TypeError("Unicode-objects must be encoded before hashing")
+ raise AlreadyFinalized("Context was already finalized.")
+ if not isinstance(data, bytes):
+ raise TypeError("data must be bytes.")
self._ctx.update(data)
def finalize(self):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
digest = self._ctx.finalize()
self._ctx = None
return digest
def verify(self, signature):
- if isinstance(signature, six.text_type):
- raise TypeError("Unicode-objects must be encoded before verifying")
+ if not isinstance(signature, bytes):
+ raise TypeError("signature must be bytes.")
digest = self.finalize()
if not constant_time.bytes_eq(digest, signature):
raise InvalidSignature("Signature did not match digest.")
def copy(self):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
return CMAC(
self._algorithm,
backend=self._backend,
diff --git a/cryptography/hazmat/primitives/constant_time.py b/cryptography/hazmat/primitives/constant_time.py
index e0e9aa37..4547da13 100644
--- a/cryptography/hazmat/primitives/constant_time.py
+++ b/cryptography/hazmat/primitives/constant_time.py
@@ -17,8 +17,6 @@ import sys
import cffi
-import six
-
from cryptography.hazmat.bindings.utils import _create_modulename
TYPES = """
@@ -57,7 +55,7 @@ _lib = _ffi.verify(
def bytes_eq(a, b):
- if isinstance(a, six.text_type) or isinstance(b, six.text_type):
- raise TypeError("Unicode-objects must be encoded before comparing")
+ 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
diff --git a/cryptography/hazmat/primitives/hashes.py b/cryptography/hazmat/primitives/hashes.py
index 35b677b0..04f7620a 100644
--- a/cryptography/hazmat/primitives/hashes.py
+++ b/cryptography/hazmat/primitives/hashes.py
@@ -13,8 +13,6 @@
from __future__ import absolute_import, division, print_function
-import six
-
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, UnsupportedAlgorithm, _Reasons
@@ -28,7 +26,7 @@ class Hash(object):
def __init__(self, algorithm, backend, ctx=None):
if not isinstance(backend, HashBackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement HashBackend",
+ "Backend object does not implement HashBackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
@@ -45,21 +43,21 @@ class Hash(object):
def update(self, data):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
- if isinstance(data, six.text_type):
- raise TypeError("Unicode-objects must be encoded before hashing")
+ raise AlreadyFinalized("Context was already finalized.")
+ if not isinstance(data, bytes):
+ raise TypeError("data must be bytes.")
self._ctx.update(data)
def copy(self):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
return Hash(
self.algorithm, backend=self._backend, ctx=self._ctx.copy()
)
def finalize(self):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
digest = self._ctx.finalize()
self._ctx = None
return digest
diff --git a/cryptography/hazmat/primitives/hmac.py b/cryptography/hazmat/primitives/hmac.py
index afbb2f75..026ad3b3 100644
--- a/cryptography/hazmat/primitives/hmac.py
+++ b/cryptography/hazmat/primitives/hmac.py
@@ -13,8 +13,6 @@
from __future__ import absolute_import, division, print_function
-import six
-
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons
@@ -28,7 +26,7 @@ class HMAC(object):
def __init__(self, key, algorithm, backend, ctx=None):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement HMACBackend",
+ "Backend object does not implement HMACBackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
@@ -45,14 +43,14 @@ class HMAC(object):
def update(self, msg):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
- if isinstance(msg, six.text_type):
- raise TypeError("Unicode-objects must be encoded before hashing")
+ raise AlreadyFinalized("Context was already finalized.")
+ if not isinstance(msg, bytes):
+ raise TypeError("msg must be bytes.")
self._ctx.update(msg)
def copy(self):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
return HMAC(
self._key,
self.algorithm,
@@ -62,14 +60,14 @@ class HMAC(object):
def finalize(self):
if self._ctx is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
digest = self._ctx.finalize()
self._ctx = None
return digest
def verify(self, signature):
- if isinstance(signature, six.text_type):
- raise TypeError("Unicode-objects must be encoded before verifying")
+ if not isinstance(signature, bytes):
+ raise TypeError("signature must be bytes.")
digest = self.finalize()
if not constant_time.bytes_eq(digest, signature):
raise InvalidSignature("Signature did not match digest.")
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
index 810a67a4..0dd1d01a 100644
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -489,3 +489,63 @@ class CMACContext(object):
"""
Return a CMACContext that is a copy of the current context.
"""
+
+
+@six.add_metaclass(abc.ABCMeta)
+class EllipticCurve(object):
+ @abc.abstractproperty
+ def name(self):
+ """
+ The name of the curve. e.g. secp256r1.
+ """
+
+ @abc.abstractproperty
+ def key_size(self):
+ """
+ The bit length of the base point of the curve.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
+class EllipticCurveSignatureAlgorithm(object):
+ @abc.abstractproperty
+ def algorithm(self):
+ """
+ The digest algorithm used with this signature.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
+class EllipticCurvePrivateKey(object):
+ @abc.abstractmethod
+ def signer(self, signature_algorithm):
+ """
+ Returns an AsymmetricSignatureContext used for signing data.
+ """
+
+ @abc.abstractmethod
+ def public_key(self):
+ """
+ The EllipticCurvePublicKey for this private key.
+ """
+
+ @abc.abstractproperty
+ def curve(self):
+ """
+ The EllipticCurve that this key is on.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
+class EllipticCurvePublicKey(object):
+ @abc.abstractmethod
+ def verifier(self, signature, signature_algorithm):
+ """
+ Returns an AsymmetricVerificationContext used for signing data.
+ """
+
+ @abc.abstractproperty
+ def curve(self):
+ """
+ The EllipticCurve that this key is on.
+ """
diff --git a/cryptography/hazmat/primitives/kdf/hkdf.py b/cryptography/hazmat/primitives/kdf/hkdf.py
index 03500aaa..04d02b26 100644
--- a/cryptography/hazmat/primitives/kdf/hkdf.py
+++ b/cryptography/hazmat/primitives/kdf/hkdf.py
@@ -28,12 +28,53 @@ class HKDF(object):
def __init__(self, algorithm, length, salt, info, backend):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement HMACBackend",
+ "Backend object does not implement HMACBackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
self._algorithm = algorithm
+ if not isinstance(salt, bytes) and salt is not None:
+ raise TypeError("salt must be bytes.")
+
+ if salt is None:
+ salt = b"\x00" * (self._algorithm.digest_size // 8)
+
+ self._salt = salt
+
+ self._backend = backend
+
+ self._hkdf_expand = HKDFExpand(self._algorithm, length, info, backend)
+
+ def _extract(self, key_material):
+ h = hmac.HMAC(self._salt, self._algorithm, backend=self._backend)
+ h.update(key_material)
+ return h.finalize()
+
+ def derive(self, key_material):
+ if not isinstance(key_material, bytes):
+ raise TypeError("key_material must be bytes.")
+
+ return self._hkdf_expand.derive(self._extract(key_material))
+
+ def verify(self, key_material, expected_key):
+ if not constant_time.bytes_eq(self.derive(key_material), expected_key):
+ raise InvalidKey
+
+
+@utils.register_interface(interfaces.KeyDerivationFunction)
+class HKDFExpand(object):
+ def __init__(self, algorithm, length, info, backend):
+ if not isinstance(backend, HMACBackend):
+ raise UnsupportedAlgorithm(
+ "Backend object does not implement HMACBackend.",
+ _Reasons.BACKEND_MISSING_INTERFACE
+ )
+
+ self._algorithm = algorithm
+
+ self._backend = backend
+
max_length = 255 * (algorithm.digest_size // 8)
if length > max_length:
@@ -44,32 +85,16 @@ class HKDF(object):
self._length = length
- if isinstance(salt, six.text_type):
- raise TypeError(
- "Unicode-objects must be encoded before using them as a salt.")
-
- if salt is None:
- salt = b"\x00" * (self._algorithm.digest_size // 8)
-
- self._salt = salt
-
- if isinstance(info, six.text_type):
- raise TypeError(
- "Unicode-objects must be encoded before using them as info.")
+ if not isinstance(info, bytes) and info is not None:
+ raise TypeError("info must be bytes.")
if info is None:
info = b""
self._info = info
- self._backend = backend
self._used = False
- def _extract(self, key_material):
- h = hmac.HMAC(self._salt, self._algorithm, backend=self._backend)
- h.update(key_material)
- return h.finalize()
-
def _expand(self, key_material):
output = [b""]
counter = 1
@@ -85,17 +110,14 @@ class HKDF(object):
return b"".join(output)[:self._length]
def derive(self, key_material):
- if isinstance(key_material, six.text_type):
- raise TypeError(
- "Unicode-objects must be encoded before using them as key "
- "material."
- )
+ if not isinstance(key_material, bytes):
+ raise TypeError("key_material must be bytes.")
if self._used:
raise AlreadyFinalized
self._used = True
- return self._expand(self._extract(key_material))
+ return self._expand(key_material)
def verify(self, key_material, expected_key):
if not constant_time.bytes_eq(self.derive(key_material), expected_key):
diff --git a/cryptography/hazmat/primitives/kdf/pbkdf2.py b/cryptography/hazmat/primitives/kdf/pbkdf2.py
index bec35bb2..97b6408c 100644
--- a/cryptography/hazmat/primitives/kdf/pbkdf2.py
+++ b/cryptography/hazmat/primitives/kdf/pbkdf2.py
@@ -13,8 +13,6 @@
from __future__ import absolute_import, division, print_function
-import six
-
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
@@ -28,38 +26,32 @@ class PBKDF2HMAC(object):
def __init__(self, algorithm, length, salt, iterations, backend):
if not isinstance(backend, PBKDF2HMACBackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement PBKDF2HMACBackend",
+ "Backend object does not implement PBKDF2HMACBackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
if not backend.pbkdf2_hmac_supported(algorithm):
raise UnsupportedAlgorithm(
- "{0} is not supported for PBKDF2 by this backend".format(
+ "{0} is not supported for PBKDF2 by this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
self._used = False
self._algorithm = algorithm
self._length = length
- if isinstance(salt, six.text_type):
- raise TypeError(
- "Unicode-objects must be encoded before using them as key "
- "material."
- )
+ if not isinstance(salt, bytes):
+ raise TypeError("salt must be bytes.")
self._salt = salt
self._iterations = iterations
self._backend = backend
def derive(self, key_material):
if self._used:
- raise AlreadyFinalized("PBKDF2 instances can only be used once")
+ raise AlreadyFinalized("PBKDF2 instances can only be used once.")
self._used = True
- if isinstance(key_material, six.text_type):
- raise TypeError(
- "Unicode-objects must be encoded before using them as key "
- "material."
- )
+ if not isinstance(key_material, bytes):
+ raise TypeError("key_material must be bytes.")
return self._backend.derive_pbkdf2_hmac(
self._algorithm,
self._length,
diff --git a/cryptography/hazmat/primitives/padding.py b/cryptography/hazmat/primitives/padding.py
index c1a763b5..74f1ef2e 100644
--- a/cryptography/hazmat/primitives/padding.py
+++ b/cryptography/hazmat/primitives/padding.py
@@ -79,10 +79,10 @@ _lib = _ffi.verify(
class PKCS7(object):
def __init__(self, block_size):
if not (0 <= block_size < 256):
- raise ValueError("block_size must be in range(0, 256)")
+ raise ValueError("block_size must be in range(0, 256).")
if block_size % 8 != 0:
- raise ValueError("block_size must be a multiple of 8")
+ raise ValueError("block_size must be a multiple of 8.")
self.block_size = block_size
@@ -102,10 +102,10 @@ class _PKCS7PaddingContext(object):
def update(self, data):
if self._buffer is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
- if isinstance(data, six.text_type):
- raise TypeError("Unicode-objects must be encoded before padding")
+ if not isinstance(data, bytes):
+ raise TypeError("data must be bytes.")
self._buffer += data
@@ -118,7 +118,7 @@ class _PKCS7PaddingContext(object):
def finalize(self):
if self._buffer is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
pad_size = self.block_size // 8 - len(self._buffer)
result = self._buffer + six.int2byte(pad_size) * pad_size
@@ -135,10 +135,10 @@ class _PKCS7UnpaddingContext(object):
def update(self, data):
if self._buffer is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
- if isinstance(data, six.text_type):
- raise TypeError("Unicode-objects must be encoded before unpadding")
+ if not isinstance(data, bytes):
+ raise TypeError("data must be bytes.")
self._buffer += data
@@ -154,17 +154,17 @@ class _PKCS7UnpaddingContext(object):
def finalize(self):
if self._buffer is None:
- raise AlreadyFinalized("Context was already finalized")
+ raise AlreadyFinalized("Context was already finalized.")
if len(self._buffer) != self.block_size // 8:
- raise ValueError("Invalid padding bytes")
+ raise ValueError("Invalid padding bytes.")
valid = _lib.Cryptography_check_pkcs7_padding(
self._buffer, self.block_size // 8
)
if not valid:
- raise ValueError("Invalid padding bytes")
+ raise ValueError("Invalid padding bytes.")
pad_size = six.indexbytes(self._buffer, -1)
res = self._buffer[:-pad_size]
diff --git a/cryptography/hazmat/primitives/serialization.py b/cryptography/hazmat/primitives/serialization.py
new file mode 100644
index 00000000..ed73c4c4
--- /dev/null
+++ b/cryptography/hazmat/primitives/serialization.py
@@ -0,0 +1,26 @@
+# 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.
+
+from __future__ import absolute_import, division, print_function
+
+
+def load_pem_traditional_openssl_private_key(data, password, backend):
+ return backend.load_traditional_openssl_pem_private_key(
+ data, password
+ )
+
+
+def load_pem_pkcs8_private_key(data, password, backend):
+ return backend.load_pkcs8_pem_private_key(
+ data, password
+ )
diff --git a/cryptography/hazmat/primitives/twofactor/hotp.py b/cryptography/hazmat/primitives/twofactor/hotp.py
index 41c467c8..d0b476a7 100644
--- a/cryptography/hazmat/primitives/twofactor/hotp.py
+++ b/cryptography/hazmat/primitives/twofactor/hotp.py
@@ -29,7 +29,7 @@ class HOTP(object):
def __init__(self, key, length, algorithm, backend):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement HMACBackend",
+ "Backend object does not implement HMACBackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
@@ -37,13 +37,13 @@ class HOTP(object):
raise ValueError("Key length has to be at least 128 bits.")
if not isinstance(length, six.integer_types):
- raise TypeError("Length parameter must be an integer type")
+ raise TypeError("Length parameter must be an integer type.")
if length < 6 or length > 8:
raise ValueError("Length of HOTP has to be between 6 to 8.")
if not isinstance(algorithm, (SHA1, SHA256, SHA512)):
- raise TypeError("Algorithm must be SHA1, SHA256 or SHA512")
+ raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.")
self._key = key
self._length = length
@@ -57,15 +57,13 @@ class HOTP(object):
def verify(self, hotp, counter):
if not constant_time.bytes_eq(self.generate(counter), hotp):
- raise InvalidToken("Supplied HOTP value does not match")
+ raise InvalidToken("Supplied HOTP value does not match.")
def _dynamic_truncate(self, counter):
ctx = hmac.HMAC(self._key, self._algorithm, self._backend)
ctx.update(struct.pack(">Q", counter))
hmac_value = ctx.finalize()
- offset_bits = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111
-
- offset = int(offset_bits)
+ offset = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111
p = hmac_value[offset:offset + 4]
return struct.unpack(">I", p)[0] & 0x7fffffff
diff --git a/cryptography/hazmat/primitives/twofactor/totp.py b/cryptography/hazmat/primitives/twofactor/totp.py
index e55ba00d..854c5163 100644
--- a/cryptography/hazmat/primitives/twofactor/totp.py
+++ b/cryptography/hazmat/primitives/twofactor/totp.py
@@ -25,7 +25,7 @@ class TOTP(object):
def __init__(self, key, length, algorithm, time_step, backend):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
- "Backend object does not implement HMACBackend",
+ "Backend object does not implement HMACBackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
@@ -38,4 +38,4 @@ class TOTP(object):
def verify(self, totp, time):
if not constant_time.bytes_eq(self.generate(time), totp):
- raise InvalidToken("Supplied TOTP value does not match")
+ raise InvalidToken("Supplied TOTP value does not match.")
diff --git a/cryptography/utils.py b/cryptography/utils.py
index 5566d123..484eec90 100644
--- a/cryptography/utils.py
+++ b/cryptography/utils.py
@@ -16,7 +16,7 @@ from __future__ import absolute_import, division, print_function
import sys
-DeprecatedIn04 = PendingDeprecationWarning
+DeprecatedIn04 = DeprecationWarning
def register_interface(iface):
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 092b9914..4fff76b5 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -5,6 +5,7 @@ invoke
iso8601
pep8-naming
pretend
+pyasn1
pytest
requests
sphinx
diff --git a/docs/development/custom-vectors/cast5.rst b/docs/development/custom-vectors/cast5.rst
index f045ec1b..97de9016 100644
--- a/docs/development/custom-vectors/cast5.rst
+++ b/docs/development/custom-vectors/cast5.rst
@@ -15,7 +15,8 @@ the following Python script was run to generate the vector files.
.. literalinclude:: /development/custom-vectors/cast5/generate_cast5.py
-Download link: :download:`generate_cast5.py </development/custom-vectors/cast5/generate_cast5.py>`
+Download link: :download:`generate_cast5.py
+</development/custom-vectors/cast5/generate_cast5.py>`
Verification
@@ -26,4 +27,5 @@ The following Go code was used to verify the vectors.
.. literalinclude:: /development/custom-vectors/cast5/verify_cast5.go
:language: go
-Download link: :download:`verify_cast5.go </development/custom-vectors/cast5/verify_cast5.go>`
+Download link: :download:`verify_cast5.go
+</development/custom-vectors/cast5/verify_cast5.go>`
diff --git a/docs/development/custom-vectors/idea.rst b/docs/development/custom-vectors/idea.rst
index c2268634..336cdf01 100644
--- a/docs/development/custom-vectors/idea.rst
+++ b/docs/development/custom-vectors/idea.rst
@@ -14,7 +14,8 @@ the following python script was run to generate the vector files.
.. literalinclude:: /development/custom-vectors/idea/generate_idea.py
-Download link: :download:`generate_idea.py </development/custom-vectors/idea/generate_idea.py>`
+Download link: :download:`generate_idea.py
+</development/custom-vectors/idea/generate_idea.py>`
Verification
@@ -25,6 +26,7 @@ project's Python bindings.
.. literalinclude:: /development/custom-vectors/idea/verify_idea.py
-Download link: :download:`verify_idea.py </development/custom-vectors/idea/verify_idea.py>`
+Download link: :download:`verify_idea.py
+</development/custom-vectors/idea/verify_idea.py>`
.. _`Botan`: http://botan.randombit.net
diff --git a/docs/development/custom-vectors/seed.rst b/docs/development/custom-vectors/seed.rst
index 5ea4295b..290fb77a 100644
--- a/docs/development/custom-vectors/seed.rst
+++ b/docs/development/custom-vectors/seed.rst
@@ -14,7 +14,8 @@ the following python script was run to generate the vector files.
.. literalinclude:: /development/custom-vectors/seed/generate_seed.py
-Download link: :download:`generate_seed.py </development/custom-vectors/seed/generate_seed.py>`
+Download link: :download:`generate_seed.py
+</development/custom-vectors/seed/generate_seed.py>`
Verification
@@ -25,6 +26,7 @@ project's Python bindings.
.. literalinclude:: /development/custom-vectors/seed/verify_seed.py
-Download link: :download:`verify_seed.py </development/custom-vectors/seed/verify_seed.py>`
+Download link: :download:`verify_seed.py
+</development/custom-vectors/seed/verify_seed.py>`
.. _`Botan`: http://botan.randombit.net
diff --git a/docs/doing-a-release.rst b/docs/doing-a-release.rst
index ad3b4791..dd62c794 100644
--- a/docs/doing-a-release.rst
+++ b/docs/doing-a-release.rst
@@ -52,3 +52,6 @@ Post-release tasks
* Check for any outstanding code undergoing a deprecation cycle by looking in
``cryptography.utils`` for ``DeprecatedIn**`` definitions. If any exist open
a ticket to increment them for the next release.
+* Send an email to the `mailing list`_ announcing the release.
+
+.. _`mailing list`: https://mail.python.org/mailman/listinfo/cryptography-dev
diff --git a/docs/faq.rst b/docs/faq.rst
index 0b7bdce4..4e8efc1d 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -14,5 +14,34 @@ to NaCl.
If you prefer NaCl's design, we highly recommend `PyNaCl`_.
+When I try to use ``cryptography`` on Windows I get a ``cffi.ffiplatform.VerificationError``
+--------------------------------------------------------------------------------------------
+
+This error looks something like:
+
+.. code-block:: console
+
+ cffi.ffiplatform.VerificationError: importing '<some_path>.pyd': DLL load failed:
+
+It typically occurs on Windows when you have not installed OpenSSL. Download
+a `pre-compiled binary`_ to resolve the issue. To select the right architecture
+(32-bit or 64-bit) open a command prompt and start your Python interpreter.
+
+If it is 32-bit it will say ``32 bit`` as well as ``Intel`` in the output:
+
+.. code-block:: console
+
+ Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
+
+If it is 64-bit you will see ``64 bit`` as well as ``AMD64``:
+
+.. code-block:: console
+
+ Python 2.7.6 (default, Nov 10 2013, 19:24:24) [MSC v.1500 64 bit (AMD64)] on win32
+
+Note that for both 32-bit and 64-bit it will say ``win32``, but other data
+in the string may vary based on your version of Python.
+
.. _`NaCl`: http://nacl.cr.yp.to/
.. _`PyNaCl`: https://pynacl.readthedocs.org
+.. _`pre-compiled binary`: https://www.openssl.org/related/binaries.html
diff --git a/docs/fernet.rst b/docs/fernet.rst
index f55a2d60..1c4918ad 100644
--- a/docs/fernet.rst
+++ b/docs/fernet.rst
@@ -34,12 +34,13 @@ symmetric (also known as "secret key") authenticated cryptography.
they'll also be able forge arbitrary messages that will be
authenticated and decrypted.
- .. method:: encrypt(plaintext)
+ .. method:: encrypt(data)
- :param bytes plaintext: The message you would like to encrypt.
+ :param bytes data: The message you would like to encrypt.
:returns bytes: A secure message that cannot be read or altered
without the key. It is URL-safe base64-encoded. This is
referred to as a "Fernet token".
+ :raises TypeError: This exception is raised if ``data`` is not ``bytes``.
.. note::
@@ -66,6 +67,7 @@ symmetric (also known as "secret key") authenticated cryptography.
``ttl``, it is malformed, or
it does not have a valid
signature.
+ :raises TypeError: This exception is raised if ``token`` is not ``bytes``.
.. class:: InvalidToken
diff --git a/docs/hazmat/backends/commoncrypto.rst b/docs/hazmat/backends/commoncrypto.rst
index 77d6612c..ddaf97e5 100644
--- a/docs/hazmat/backends/commoncrypto.rst
+++ b/docs/hazmat/backends/commoncrypto.rst
@@ -3,8 +3,8 @@
CommonCrypto backend
====================
-The `CommonCrypto`_ C library provided by Apple on OS X and iOS. The CommonCrypto
-backend is only supported on OS X versions 10.8 and above.
+The `CommonCrypto`_ C library provided by Apple on OS X and iOS. The
+CommonCrypto backend is only supported on OS X versions 10.8 and above.
.. currentmodule:: cryptography.hazmat.backends.commoncrypto.backend
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index 2f63f3e0..c1ce621a 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -6,8 +6,8 @@ Backend interfaces
.. currentmodule:: cryptography.hazmat.backends.interfaces
-Backend implementations may provide a number of interfaces to support operations
-such as :doc:`/hazmat/primitives/symmetric-encryption`,
+Backend implementations may provide a number of interfaces to support
+operations such as :doc:`/hazmat/primitives/symmetric-encryption`,
:doc:`/hazmat/primitives/cryptographic-hashes`, and
:doc:`/hazmat/primitives/mac/hmac`.
@@ -263,6 +263,26 @@ A specific ``backend`` may provide one or more of these interfaces.
:returns: ``True`` if the specified ``algorithm`` is supported by this
backend, otherwise ``False``.
+ .. method:: rsa_padding_supported(padding)
+
+ Check if the specified ``padding`` is supported by the backend.
+
+ :param padding: An instance of an
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
+ provider.
+
+ :returns: ``True`` if the specified ``padding`` is supported by this
+ backend, otherwise ``False``.
+
+ .. method:: generate_rsa_parameters_supported(public_exponent, key_size)
+
+ Check if the specified parameters are supported for key generation by
+ the backend.
+
+ :param int public_exponent: The public exponent.
+
+ :param int key_size: The bit length of the generated modulus.
+
.. method:: decrypt_rsa(private_key, ciphertext, padding)
:param private_key: An instance of an
@@ -275,6 +295,14 @@ A specific ``backend`` may provide one or more of these interfaces.
:class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
provider.
+ :return bytes: The decrypted data.
+
+ :raises cryptography.exceptions.UnsupportedAlgorithm: If an unsupported
+ MGF, hash function, or padding is chosen.
+
+ :raises ValueError: When decryption fails or key size does not match
+ ciphertext length.
+
.. method:: encrypt_rsa(public_key, plaintext, padding)
:param public_key: An instance of an
@@ -287,6 +315,12 @@ A specific ``backend`` may provide one or more of these interfaces.
:class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
provider.
+ :return bytes: The encrypted data.
+
+ :raises cryptography.exceptions.UnsupportedAlgorithm: If an unsupported
+ MGF, hash function, or padding is chosen.
+
+ :raises ValueError: When plaintext is too long for the key size.
.. class:: TraditionalOpenSSLSerializationBackend
@@ -345,6 +379,55 @@ A specific ``backend`` may provide one or more of these interfaces.
1.0.0 and the key size is larger than 1024; older OpenSSL versions
do not support keys larger than 1024 bits.
+ .. method:: create_dsa_signature_ctx(private_key, algorithm)
+
+ :param private_key: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.DSAPrivateKey`
+ provider.
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider
+
+ :returns:
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext`
+
+ .. method:: create_dsa_verification_ctx(public_key, signature, algorithm)
+
+ :param public_key: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.DSAPublicKey`
+ provider.
+
+ :param bytes signature: The signature to verify. DER encoded as
+ specified in :rfc:`6979`.
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider.
+
+ :returns:
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricVerificationContext`
+
+ .. method:: dsa_hash_supported(algorithm):
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider.
+
+ :returns: ``True`` if the specified ``algorithm`` is supported by this
+ backend, otherwise ``False``.
+
+ .. method:: dsa_parameters_supported(p, q, g):
+
+ :param int p: The p value of a DSA key.
+
+ :param int q: The q value of a DSA key.
+
+ :param int g: The g value of a DSA key.
+
+ :returns: ``True`` if the given values of ``p``, ``q``, and ``g`` are
+ supported by this backend, otherwise ``False``.
+
.. class:: CMACBackend
@@ -371,3 +454,76 @@ A specific ``backend`` may provide one or more of these interfaces.
:returns:
:class:`~cryptography.hazmat.primitives.interfaces.CMACContext`
+
+
+.. class:: PKCS8SerializationBackend
+
+ .. versionadded:: 0.5
+
+ A backend with methods for working with PKCS #8 key serialization.
+
+ .. method:: load_pkcs8_pem_private_key(data, password)
+
+ :param bytes data: PEM data to deserialize.
+
+ :param bytes password: The password to use if this data is encrypted.
+ Should be None if the data is not encrypted.
+
+ :return: A new instance of the appropriate private key or public key
+ that the serialized data contains.
+
+ :raises ValueError: If the data could not be deserialized correctly.
+
+ :raises cryptography.exceptions.UnsupportedAlgorithm: If the data is
+ encrypted with an unsupported algorithm.
+
+
+.. class:: EllipticCurveBackend
+
+ .. versionadded:: 0.5
+
+ .. method:: elliptic_curve_supported(curve)
+
+ :param curve: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurve`
+ provider.
+
+ :returns: True if the elliptic curve is supported by this backend.
+
+ .. method:: elliptic_curve_signature_algorithm_supported(signature_algorithm, curve)
+
+ :param signature_algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurveSignatureAlgorithm`
+ provider.
+
+ :param curve: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurve`
+ provider.
+
+ :returns: True if the signature algorithm and curve are supported by this backend.
+
+ .. method:: generate_elliptic_curve_private_key(curve)
+
+ :param curve: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurve`
+ provider.
+
+ .. method:: elliptic_curve_private_key_from_numbers(numbers)
+
+ :param numbers: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurvePrivateNumbers`
+ provider.
+
+ :returns: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurvePrivateKey`
+ provider.
+
+ .. method:: elliptic_curve_public_key_from_numbers(numbers)
+
+ :param numbers: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurvePublicNumbers`
+ provider.
+
+ :returns: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey`
+ provider.
diff --git a/docs/hazmat/backends/openssl.rst b/docs/hazmat/backends/openssl.rst
index 43e5d8f3..e829798a 100644
--- a/docs/hazmat/backends/openssl.rst
+++ b/docs/hazmat/backends/openssl.rst
@@ -14,10 +14,14 @@ Red Hat Enterprise Linux 5) and greater. Earlier versions may work but are
It implements the following interfaces:
* :class:`~cryptography.hazmat.backends.interfaces.CipherBackend`
+ * :class:`~cryptography.hazmat.backends.interfaces.CMACBackend`
+ * :class:`~cryptography.hazmat.backends.interfaces.DSABackend`
* :class:`~cryptography.hazmat.backends.interfaces.HashBackend`
* :class:`~cryptography.hazmat.backends.interfaces.HMACBackend`
* :class:`~cryptography.hazmat.backends.interfaces.PBKDF2HMACBackend`
+ * :class:`~cryptography.hazmat.backends.interfaces.PKCS8SerializationBackend`
* :class:`~cryptography.hazmat.backends.interfaces.RSABackend`
+ * :class:`~cryptography.hazmat.backends.interfaces.TraditionalOpenSSLSerializationBackend`
It also exposes the following:
@@ -44,9 +48,9 @@ where two different processes can return similar or identical keys and
compromise the security of the system.
The approach this project has chosen to mitigate this vulnerability is to
-include an engine that replaces the OpenSSL default CSPRNG with one that sources
-its entropy from ``/dev/urandom`` on UNIX-like operating systems and uses
-``CryptGenRandom`` on Windows. This method of pulling from the system pool
+include an engine that replaces the OpenSSL default CSPRNG with one that
+sources its entropy from ``/dev/urandom`` on UNIX-like operating systems and
+uses ``CryptGenRandom`` on Windows. This method of pulling from the system pool
allows us to avoid potential issues with `initializing the RNG`_ as well as
protecting us from the ``fork()`` weakness.
@@ -68,8 +72,8 @@ On Windows the implementation of ``CryptGenRandom`` depends on which version of
the operation system you are using. See the `Microsoft documentation`_ for more
details.
-Linux uses its own PRNG design. ``/dev/urandom`` is a non-blocking source seeded
-from the same pool as ``/dev/random``.
+Linux uses its own PRNG design. ``/dev/urandom`` is a non-blocking source
+seeded from the same pool as ``/dev/random``.
.. _`OpenSSL`: https://www.openssl.org/
diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst
index 2819bbdb..6848d84c 100644
--- a/docs/hazmat/primitives/asymmetric/dsa.rst
+++ b/docs/hazmat/primitives/asymmetric/dsa.rst
@@ -38,11 +38,11 @@ DSA
Generate a new ``DSAParameters`` instance using ``backend``.
:param int key_size: The length of the modulus in bits. It should be
- either "1024, 2048 or 3072". For keys generated in 2014 this should
- be `at least 2048`_ (See page 41).
- Note that some applications (such as SSH) have not yet gained support
- for larger key sizes specified in FIPS 186-3 and are still restricted
- to only the 1024-bit keys specified in FIPS 186-2.
+ either 1024, 2048 or 3072. For keys generated in 2014 this should
+ be `at least 2048`_ (See page 41). Note that some applications
+ (such as SSH) have not yet gained support for larger key sizes
+ specified in FIPS 186-3 and are still restricted to only the
+ 1024-bit keys specified in FIPS 186-2.
:return: A new instance of ``DSAParameters``
@@ -97,6 +97,48 @@ DSA
or if the OpenSSL version is older than 1.0.0 and the key size is larger than 1024
because older OpenSSL versions don't support a key size larger than 1024.
+ .. method:: signer(algorithm, backend)
+
+ .. versionadded:: 0.4
+
+ Sign data which can be verified later by others using the public key.
+
+ .. doctest::
+
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> from cryptography.hazmat.primitives.asymmetric import dsa
+ >>> parameters = dsa.DSAParameters.generate(
+ ... key_size=1024,
+ ... backend=default_backend()
+ ... )
+ >>> private_key = dsa.DSAPrivateKey.generate(
+ ... parameters=parameters,
+ ... backend=default_backend()
+ ... )
+ >>> signer = private_key.signer(
+ ... hashes.SHA256(),
+ ... default_backend()
+ ... )
+ >>> data = b"this is some data I'd like to sign"
+ >>> signer.update(data)
+ >>> signature = signer.finalize()
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider.
+
+ :param backend: A
+ :class:`~cryptography.hazmat.backends.interfaces.RSABackend`
+ provider.
+
+ :returns:
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext`
+
+ :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if
+ the provided ``backend`` does not implement
+ :class:`~cryptography.hazmat.backends.interfaces.DSABackend`
+
.. class:: DSAPublicKey(modulus, subgroup_order, generator, y)
@@ -118,6 +160,55 @@ DSA
``subgroup_order``, ``generator``, or ``y``
do not match the bounds specified in `FIPS 186-4`_.
+ .. method:: verifier(signature, algorithm, backend)
+
+ .. versionadded:: 0.4
+
+ Verify data was signed by the private key associated with this public
+ key.
+
+ .. doctest::
+
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> from cryptography.hazmat.primitives.asymmetric import dsa
+ >>> parameters = dsa.DSAParameters.generate(
+ ... key_size=1024,
+ ... backend=default_backend()
+ ... )
+ >>> private_key = dsa.DSAPrivateKey.generate(
+ ... parameters=parameters,
+ ... backend=default_backend()
+ ... )
+ >>> signer = private_key.signer(
+ ... hashes.SHA256(),
+ ... default_backend()
+ ... )
+ >>> data = b"this is some data I'd like to sign"
+ >>> signer.update(data)
+ >>> signature = signer.finalize()
+ >>> public_key = private_key.public_key()
+ >>> verifier = public_key.verifier(
+ ... signature,
+ ... hashes.SHA256(),
+ ... default_backend()
+ ... )
+ >>> verifier.update(data)
+ >>> verifier.verify()
+
+ :param bytes signature: The signature to verify. DER encoded as
+ specified in :rfc:`6979`.
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider.
+
+ :param backend: A
+ :class:`~cryptography.hazmat.backends.interfaces.DSABackend`
+ provider.
+
+ :returns:
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricVerificationContext`
.. _`DSA`: https://en.wikipedia.org/wiki/Digital_Signature_Algorithm
.. _`public-key`: https://en.wikipedia.org/wiki/Public-key_cryptography
diff --git a/docs/hazmat/primitives/asymmetric/ec.rst b/docs/hazmat/primitives/asymmetric/ec.rst
new file mode 100644
index 00000000..f88b965a
--- /dev/null
+++ b/docs/hazmat/primitives/asymmetric/ec.rst
@@ -0,0 +1,51 @@
+.. hazmat::
+
+Elliptic Curve
+==============
+
+.. currentmodule:: cryptography.hazmat.primitives.asymmetric.ec
+
+
+.. class:: EllipticCurvePrivateNumbers(private_value, public_numbers)
+
+ .. versionadded:: 0.5
+
+ The collection of integers that make up an EC private key.
+
+ .. attribute:: public_numbers
+
+ :type: :class:`~cryptography.hazmat.primitives.ec.EllipticCurvePublicNumbers`
+
+ The :class:`EllipticCurvePublicNumbers` which makes up the EC public
+ key associated with this EC private key.
+
+ .. attribute:: private_value
+
+ :type: int
+
+ The private value.
+
+
+.. class:: EllipticCurvePublicNumbers(x, y, curve)
+
+ .. versionadded:: 0.5
+
+ The collection of integers that make up an EC public key.
+
+ .. attribute:: curve
+
+ :type: :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurve`
+
+ The elliptic curve for this key.
+
+ .. attribute:: x
+
+ :type: int
+
+ The affine x component of the public point used for verifying.
+
+ .. attribute:: y
+
+ :type: int
+
+ The affine y component of the public point used for verifying.
diff --git a/docs/hazmat/primitives/asymmetric/index.rst b/docs/hazmat/primitives/asymmetric/index.rst
index ca048d11..6a5228ba 100644
--- a/docs/hazmat/primitives/asymmetric/index.rst
+++ b/docs/hazmat/primitives/asymmetric/index.rst
@@ -7,5 +7,7 @@ Asymmetric algorithms
:maxdepth: 1
dsa
+ ec
rsa
padding
+ serialization
diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst
index 8c34497e..54839119 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -85,7 +85,10 @@ RSA
:param padding: An instance of a
:class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
- provider.
+ provider. Valid values are
+ :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS` and
+ :class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`
+ (``PSS`` is recommended for all new applications).
:param algorithm: An instance of a
:class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
@@ -154,21 +157,39 @@ RSA
:class:`~cryptography.hazmat.primitives.asymmetric.padding.OAEP`
it may also be raised for invalid label values.
- .. code-block:: python
+ .. doctest::
- from cryptography.hazmat.backends import default_backend
- from cryptography.hazmat.primitives import hashes
- from cryptography.hazmat.primitives.asymmetric import padding
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> from cryptography.hazmat.primitives.asymmetric import padding
- plaintext = private_key.decrypt(
- ciphertext,
- padding.OAEP(
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
- algorithm=hashes.SHA1(),
- label=None
- ),
- default_backend()
- )
+ >>> # Generate a key
+ >>> private_key = rsa.RSAPrivateKey.generate(
+ ... public_exponent=65537,
+ ... key_size=2048,
+ ... backend=default_backend()
+ ... )
+ >>> public_key = private_key.public_key()
+ >>> # encrypt some data
+ >>> ciphertext = public_key.encrypt(
+ ... b"encrypted data",
+ ... padding.OAEP(
+ ... mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ ... algorithm=hashes.SHA1(),
+ ... label=None
+ ... ),
+ ... default_backend()
+ ... )
+ >>> # Now do the actual decryption
+ >>> plaintext = private_key.decrypt(
+ ... ciphertext,
+ ... padding.OAEP(
+ ... mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ ... algorithm=hashes.SHA1(),
+ ... label=None
+ ... ),
+ ... default_backend()
+ ... )
.. class:: RSAPublicKey(public_exponent, modulus)
@@ -216,7 +237,7 @@ RSA
... hashes.SHA256(),
... default_backend()
... )
- >>> data= b"this is some data I'd like to sign"
+ >>> data = b"this is some data I'd like to sign"
>>> signer.update(data)
>>> signature = signer.finalize()
>>> public_key = private_key.public_key()
@@ -236,7 +257,10 @@ RSA
:param padding: An instance of a
:class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
- provider.
+ provider. Valid values are
+ :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS` and
+ :class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`
+ (``PSS`` is recommended for all new applications).
:param algorithm: An instance of a
:class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
@@ -306,27 +330,29 @@ RSA
:class:`~cryptography.hazmat.primitives.asymmetric.padding.OAEP`
it may also be raised for invalid label values.
- .. code-block:: python
-
- from cryptography.hazmat.backends import default_backend
- from cryptography.hazmat.primitives import hashes
- from cryptography.hazmat.primitives.asymmetric import padding, rsa
-
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=2048,
- backend=default_backend()
- )
- public_key = private_key.public_key()
- ciphertext = public_key.encrypt(
- plaintext,
- padding.OAEP(
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
- algorithm=hashes.SHA1(),
- label=None
- ),
- default_backend()
- )
+ .. doctest::
+
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> from cryptography.hazmat.primitives.asymmetric import padding
+
+ >>> # Generate a key
+ >>> private_key = rsa.RSAPrivateKey.generate(
+ ... public_exponent=65537,
+ ... key_size=2048,
+ ... backend=default_backend()
+ ... )
+ >>> public_key = private_key.public_key()
+ >>> # encrypt some data
+ >>> ciphertext = public_key.encrypt(
+ ... b"encrypted data",
+ ... padding.OAEP(
+ ... mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ ... algorithm=hashes.SHA1(),
+ ... label=None
+ ... ),
+ ... default_backend()
+ ... )
.. class:: RSAPublicNumbers(e, n)
diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst
new file mode 100644
index 00000000..2b3eb511
--- /dev/null
+++ b/docs/hazmat/primitives/asymmetric/serialization.rst
@@ -0,0 +1,100 @@
+.. hazmat::
+
+Key Serialization
+=================
+
+.. currentmodule:: cryptography.hazmat.primitives.serialization
+
+There are several common schemes for serializing asymmetric private and public
+keys to bytes. They generally support encryption of private keys and additional
+key metadata.
+
+Many serialization formats support multiple different types of asymmetric keys
+and will return an an instance of the appropriate type. You should check that
+the returned key matches the type your application expects when using these
+methods.
+
+ .. code-block:: pycon
+
+ >>> key = load_pkcs8_private_key(pem_data, None, backend)
+ >>> if isinstance(key, rsa.RSAPrivateKey):
+ >>> signature = sign_with_rsa_key(key, message)
+ >>> elif isinstance(key, dsa.DSAPrivateKey):
+ >>> signature = sign_with_dsa_key(key, message)
+ >>> else:
+ >>> raise TypeError
+
+
+PKCS #8 Format
+~~~~~~~~~~~~~~
+
+PKCS #8 is a serialization format originally standardized by RSA and
+currently maintained by the IETF in :rfc:`5208`. It supports password based
+encryption and additional key metadata attributes.
+
+
+.. function:: load_pkcs8_private_key(data, password, backend)
+
+ .. versionadded:: 0.5
+
+ Deserialize a private key from PEM encoded data to one of the supported
+ asymmetric private key types.
+
+ :param bytes data: The PEM encoded key data.
+
+ :param bytes password: The password to use to decrypt the data. Should
+ be ``None`` if the private key is not encrypted.
+ :param backend: A
+ :class:`~cryptography.hazmat.backends.interfaces.PKCS8SerializationBackend`
+ provider.
+
+ :returns: A new instance of a private key.
+
+ :raises ValueError: If the PEM data could not be decrypted or if its
+ structure could not be decoded successfully.
+
+ :raises TypeError: If a ``password`` was given and the private key was
+ not encrypted. Or if the key was encrypted but no
+ password was supplied.
+
+ :raises UnsupportedAlgorithm: If the serialized key is of a type that
+ is not supported by the backend or if the key is encrypted with a
+ symmetric cipher that is not supported by the backend.
+
+
+Traditional OpenSSL Format
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The "traditional" PKCS #1 based serialization format used by OpenSSL.
+It supports password based symmetric key encryption. Commonly found in
+OpenSSL based TLS applications. It is usually found in PEM format with a
+header that mentions the type of the serialized key. e.g.
+``-----BEGIN RSA PRIVATE KEY-----``.
+
+.. function:: load_pem_traditional_openssl_private_key(data, password, backend)
+
+ .. versionadded:: 0.5
+
+ Deserialize a private key from PEM encoded data to one of the supported
+ asymmetric private key types.
+
+ :param bytes data: The PEM encoded key data.
+
+ :param bytes password: The password to use to decrypt the data. Should
+ be ``None`` if the private key is not encrypted.
+ :param backend: A
+ :class:`~cryptography.hazmat.backends.interfaces.TraditionalOpenSSLSerializationBackend`
+ provider.
+
+ :returns: A new instance of a private key.
+
+ :raises ValueError: If the PEM data could not be decrypted or if its
+ structure could not be decoded successfully.
+
+ :raises TypeError: If a ``password`` was given and the private key was
+ not encrypted. Or if the key was encrypted but no
+ password was supplied.
+
+ :raises UnsupportedAlgorithm: If the serialized key is of a type that
+ is not supported by the backend or if the key is encrypted with a
+ symmetric cipher that is not supported by the backend.
diff --git a/docs/hazmat/primitives/constant-time.rst b/docs/hazmat/primitives/constant-time.rst
index c6fcb3a3..1394b6b3 100644
--- a/docs/hazmat/primitives/constant-time.rst
+++ b/docs/hazmat/primitives/constant-time.rst
@@ -36,6 +36,8 @@ about the timing attacks on KeyCzar and Java's ``MessageDigest.isEqual()``.
:param bytes b: The right-hand side.
:returns bool: ``True`` if ``a`` has the same bytes as ``b``, otherwise
``False``.
+ :raises TypeError: This exception is raised if ``a`` or ``b`` is not
+ ``bytes``.
.. _`Coda Hale's blog post`: http://codahale.com/a-lesson-in-timing-attacks/
diff --git a/docs/hazmat/primitives/cryptographic-hashes.rst b/docs/hazmat/primitives/cryptographic-hashes.rst
index 773d97f6..7e5295c4 100644
--- a/docs/hazmat/primitives/cryptographic-hashes.rst
+++ b/docs/hazmat/primitives/cryptographic-hashes.rst
@@ -54,6 +54,7 @@ Message digests
:param bytes data: The bytes to be hashed.
:raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`.
+ :raises TypeError: This exception is raised if ``data`` is not ``bytes``.
.. method:: copy()
diff --git a/docs/hazmat/primitives/interfaces.rst b/docs/hazmat/primitives/interfaces.rst
index c76582c0..b2857f58 100644
--- a/docs/hazmat/primitives/interfaces.rst
+++ b/docs/hazmat/primitives/interfaces.rst
@@ -381,6 +381,23 @@ Asymmetric interfaces
The DSAParameters object associated with this private key.
+ .. method:: signer(algorithm, backend)
+
+ .. versionadded:: 0.4
+
+ Sign data which can be verified later by others using the public key.
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider.
+
+ :param backend: A
+ :class:`~cryptography.hazmat.backends.interfaces.DSABackend`
+ provider.
+
+ :returns:
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext`
+
.. attribute:: key_size
:type: int
@@ -412,17 +429,129 @@ Asymmetric interfaces
The bit length of the modulus.
+ .. attribute:: y
+
+ :type: int
+
+ The public key.
+
.. method:: parameters()
:return: :class:`~cryptography.hazmat.primitives.interfaces.DSAParameters`
The DSAParameters object associated with this public key.
- .. attribute:: y
+ .. method:: verifier(signature, algorithm, backend)
+
+ .. versionadded:: 0.4
+
+ Verify data was signed by the private key associated with this public
+ key.
+
+ :param bytes signature: The signature to verify. DER encoded as
+ specified in :rfc:`6979`.
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider.
+
+ :param backend: A
+ :class:`~cryptography.hazmat.backends.interfaces.DSABackend`
+ provider.
+
+ :returns:
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricVerificationContext`
+
+
+.. class:: EllipticCurve
+
+ .. versionadded:: 0.5
+
+ A named elliptic curve.
+
+ .. attribute:: name
+
+ :type: string
+
+ The name of the curve. Usually the name used for the ASN.1 OID such as
+ ``secp256k1``.
+
+ .. attribute:: key_size
:type: int
- The public key.
+ The bit length of the curve's base point.
+
+
+.. class:: EllipticCurveSignatureAlgorithm
+
+ .. versionadded:: 0.5
+
+ A signature algorithm for use with elliptic curve keys.
+
+ .. attribute:: algorithm
+
+ :type: :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+
+ The digest algorithm to be used with the signature scheme.
+
+
+.. class:: EllipticCurvePrivateKey
+
+ .. versionadded:: 0.5
+
+ An elliptic curve private key for use with an algorithm such as `ECDSA`_ or
+ `EdDSA`_.
+
+ .. classmethod:: signer(signature_algorithm)
+
+ Sign data which can be verified later by others using the public key.
+
+ :param signature_algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurveSignatureAlgorithm`
+ provider.
+
+ :returns:
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext`
+
+ .. attribute:: curve
+
+ :type: :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurve`
+
+ The elliptic curve for this key.
+
+ .. method:: public_key()
+
+ :return: :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey`
+
+ The EllipticCurvePublicKey object for this private key.
+
+
+.. class:: EllipticCurvePublicKey
+
+ .. versionadded:: 0.5
+
+ An elliptic curve public key.
+
+ .. classmethod:: verifier(signer, signature_algorithm)
+
+ Verify data was signed by the private key associated with this public
+ key.
+
+ :param bytes signature: The signature to verify.
+
+ :param signature_algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurveSignatureAlgorithm`
+ provider.
+
+ :returns:
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext`
+
+ .. attribute:: curve
+
+ :type: :class:`~cryptography.hazmat.primitives.interfaces.EllipticCurve`
+
+ The elliptic curve for this key.
.. class:: AsymmetricSignatureContext
@@ -574,3 +703,5 @@ Key derivation functions
.. _`Chinese remainder theorem`: https://en.wikipedia.org/wiki/Chinese_remainder_theorem
.. _`DSA`: https://en.wikipedia.org/wiki/Digital_Signature_Algorithm
.. _`CMAC`: https://en.wikipedia.org/wiki/CMAC
+.. _`ECDSA`: http://en.wikipedia.org/wiki/ECDSA
+.. _`EdDSA`: http://en.wikipedia.org/wiki/EdDSA
diff --git a/docs/hazmat/primitives/key-derivation-functions.rst b/docs/hazmat/primitives/key-derivation-functions.rst
index 269f949d..f68b12c1 100644
--- a/docs/hazmat/primitives/key-derivation-functions.rst
+++ b/docs/hazmat/primitives/key-derivation-functions.rst
@@ -88,6 +88,8 @@ Different KDFs are suitable for different tasks such as:
provided ``backend`` does not implement
:class:`~cryptography.hazmat.backends.interfaces.PBKDF2HMACBackend`
+ :raises TypeError: This exception is raised if ``salt`` is not ``bytes``.
+
.. method:: derive(key_material)
:param bytes key_material: The input key material. For PBKDF2 this
@@ -99,6 +101,9 @@ Different KDFs are suitable for different tasks such as:
called more than
once.
+ :raises TypeError: This exception is raised if ``key_material`` is not
+ ``bytes``.
+
This generates and returns a new key from the supplied password.
.. method:: verify(key_material, expected_key)
@@ -191,10 +196,108 @@ Different KDFs are suitable for different tasks such as:
provided ``backend`` does not implement
:class:`~cryptography.hazmat.backends.interfaces.HMACBackend`
+ :raises TypeError: This exception is raised if ``salt`` or ``info`` is not
+ ``bytes``.
+
+ .. method:: derive(key_material)
+
+ :param bytes key_material: The input key material.
+ :return bytes: The derived key.
+ :raises TypeError: This exception is raised if ``key_material`` is not
+ ``bytes``.
+
+ Derives a new key from the input key material by performing both the
+ extract and expand operations.
+
+ .. method:: verify(key_material, expected_key)
+
+ :param key_material bytes: The input key material. This is the same as
+ ``key_material`` in :meth:`derive`.
+ :param expected_key bytes: The expected result of deriving a new key,
+ this is the same as the return value of
+ :meth:`derive`.
+ :raises cryptography.exceptions.InvalidKey: This is raised when the
+ derived key does not match
+ the expected key.
+ :raises cryptography.exceptions.AlreadyFinalized: This is raised when
+ :meth:`derive` or
+ :meth:`verify` is
+ called more than
+ once.
+
+ This checks whether deriving a new key from the supplied
+ ``key_material`` generates the same key as the ``expected_key``, and
+ raises an exception if they do not match.
+
+
+.. class:: HKDFExpand(algorithm, length, info, backend)
+
+ .. versionadded:: 0.5
+
+ HKDF consists of two stages, extract and expand. This class exposes an
+ expand only version of HKDF that is suitable when the key material is
+ already cryptographically strong.
+
+ .. warning::
+
+ HKDFExpand should only be used if the key material is
+ cryptographically strong. You should use
+ :class:`~cryptography.hazmat.primitives.kdf.hkdf.HKDF` if
+ you are unsure.
+
+ .. doctest::
+
+ >>> import os
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> from cryptography.hazmat.primitives.kdf.hkdf import HKDFExpand
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> backend = default_backend()
+ >>> info = b"hkdf-example"
+ >>> key_material = os.urandom(16)
+ >>> hkdf = HKDFExpand(
+ ... algorithm=hashes.SHA256(),
+ ... length=32,
+ ... info=info,
+ ... backend=backend
+ ... )
+ >>> key = hkdf.derive(key_material)
+ >>> hkdf = HKDFExpand(
+ ... algorithm=hashes.SHA256(),
+ ... length=32,
+ ... info=info,
+ ... backend=backend
+ ... )
+ >>> hkdf.verify(key_material, key)
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider.
+
+ :param int length: The desired length of the derived key. Maximum is
+ ``255 * (algorithm.digest_size // 8)``.
+
+ :param bytes info: Application specific context information. If ``None``
+ is explicitly passed an empty byte string will be used.
+
+ :param backend: A
+ :class:`~cryptography.hazmat.backends.interfaces.HMACBackend`
+ provider.
+
+ :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if the
+ provided ``backend`` does not implement
+ :class:`~cryptography.hazmat.backends.interfaces.HMACBackend`
+ :raises TypeError: This is raised if the provided ``info`` is a unicode object
+ :raises TypeError: This exception is raised if ``info`` is not ``bytes``.
+
.. method:: derive(key_material)
:param bytes key_material: The input key material.
- :retunr bytes: The derived key.
+ :return bytes: The derived key.
+
+ :raises TypeError: This is raised if the provided ``key_material`` is
+ a unicode object
+ :raises TypeError: This exception is raised if ``key_material`` is not
+ ``bytes``.
Derives a new key from the input key material by performing both the
extract and expand operations.
@@ -214,6 +317,8 @@ Different KDFs are suitable for different tasks such as:
:meth:`verify` is
called more than
once.
+ :raises TypeError: This is raised if the provided ``key_material`` is
+ a unicode object
This checks whether deriving a new key from the supplied
``key_material`` generates the same key as the ``expected_key``, and
diff --git a/docs/hazmat/primitives/mac/cmac.rst b/docs/hazmat/primitives/mac/cmac.rst
index a6b048b5..498b8b1e 100644
--- a/docs/hazmat/primitives/mac/cmac.rst
+++ b/docs/hazmat/primitives/mac/cmac.rst
@@ -10,8 +10,8 @@ Cipher-based message authentication code
import binascii
key = binascii.unhexlify(b"0" * 32)
-`Cipher-based message authentication codes`_ (or CMACs) are a tool for calculating
-message authentication codes using a block cipher coupled with a
+`Cipher-based message authentication codes`_ (or CMACs) are a tool for
+calculating message authentication codes using a block cipher coupled with a
secret key. You can use an CMAC to verify both the integrity and authenticity
of a message.
@@ -38,9 +38,9 @@ A subset of CMAC with the AES-128 algorithm is described in :rfc:`4493`.
:class:`~cryptography.exceptions.UnsupportedAlgorithm` exception will be
raised.
- If the `algorithm`` isn't a
- :class:`~cryptography.primitives.interfaces.BlockCipherAlgorithm` provider,
- ``TypeError`` will be raised.
+ If ``algorithm`` isn't a
+ :class:`~cryptography.hazmat.primitives.interfaces.BlockCipherAlgorithm`
+ provider then ``TypeError`` will be raised.
To check that a given signature is correct use the :meth:`verify` method.
You will receive an exception if the signature is wrong:
@@ -68,6 +68,7 @@ A subset of CMAC with the AES-128 algorithm is described in :rfc:`4493`.
:param bytes data: The bytes to hash and authenticate.
:raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
+ :raises TypeError: This exception is raised if ``data`` is not ``bytes``.
.. method:: copy()
@@ -89,6 +90,8 @@ A subset of CMAC with the AES-128 algorithm is described in :rfc:`4493`.
:raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
:raises cryptography.exceptions.InvalidSignature: If signature does not
match digest
+ :raises TypeError: This exception is raised if ``signature`` is not
+ ``bytes``.
.. method:: finalize()
diff --git a/docs/hazmat/primitives/mac/hmac.rst b/docs/hazmat/primitives/mac/hmac.rst
index 11b10735..d56927b9 100644
--- a/docs/hazmat/primitives/mac/hmac.rst
+++ b/docs/hazmat/primitives/mac/hmac.rst
@@ -38,6 +38,10 @@ of a message.
:class:`~cryptography.exceptions.UnsupportedAlgorithm` exception will be
raised.
+ If ``algorithm`` isn't a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm` provider
+ then ``TypeError`` will be raised.
+
To check that a given signature is correct use the :meth:`verify` method.
You will receive an exception if the signature is wrong:
@@ -65,6 +69,7 @@ of a message.
:param bytes msg: The bytes to hash and authenticate.
:raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
+ :raises TypeError: This exception is raised if ``msg`` is not ``bytes``.
.. method:: copy()
@@ -86,6 +91,8 @@ of a message.
:raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`
:raises cryptography.exceptions.InvalidSignature: If signature does not
match digest
+ :raises TypeError: This exception is raised if ``signature`` is not
+ ``bytes``.
.. method:: finalize()
diff --git a/docs/hazmat/primitives/mac/index.rst b/docs/hazmat/primitives/mac/index.rst
index 59fb8da2..acfe9bed 100644
--- a/docs/hazmat/primitives/mac/index.rst
+++ b/docs/hazmat/primitives/mac/index.rst
@@ -3,6 +3,14 @@
Message Authentication Codes
============================
+While cryptography supports both the CMAC and HMAC algorithms, we strongly
+recommend that HMAC should be used unless you have a good reason otherwise.
+
+For more information on why HMAC is preferred, see `Use cases for CMAC vs.
+HMAC?`_
+
+.. _`Use cases for CMAC vs. HMAC?`: http://crypto.stackexchange.com/questions/15721/use-cases-for-cmac-vs-hmac
+
.. toctree::
:maxdepth: 1
diff --git a/docs/hazmat/primitives/padding.rst b/docs/hazmat/primitives/padding.rst
index 4092ac00..0322f9d2 100644
--- a/docs/hazmat/primitives/padding.rst
+++ b/docs/hazmat/primitives/padding.rst
@@ -70,6 +70,7 @@ multiple of the block size.
:return bytes: Returns the data that was padded or unpadded.
:raises TypeError: Raised if data is not bytes.
:raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize`.
+ :raises TypeError: This exception is raised if ``data`` is not ``bytes``.
.. method:: finalize()
diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst
index c98b3814..bca78354 100644
--- a/docs/hazmat/primitives/symmetric-encryption.rst
+++ b/docs/hazmat/primitives/symmetric-encryption.rst
@@ -20,9 +20,9 @@ provides secrecy but not authenticity. That means an attacker can't see the
message but an attacker can create bogus messages and force the application to
decrypt them.
-For this reason it is *strongly* recommended to combine encryption with a
-message authentication code, such as :doc:`HMAC </hazmat/primitives/mac/hmac>`, in
-an "encrypt-then-MAC" formulation as `described by Colin Percival`_.
+For this reason it is **strongly** recommended to combine encryption with a
+message authentication code, such as :doc:`HMAC </hazmat/primitives/mac/hmac>`,
+in an "encrypt-then-MAC" formulation as `described by Colin Percival`_.
.. class:: Cipher(algorithm, mode, backend)
@@ -134,8 +134,8 @@ Algorithms
.. versionadded:: 0.4
- SEED is a block cipher developed by the Korea Information Security Agency (
- KISA). It is defined in :rfc:`4269` and is used broadly throughout South
+ SEED is a block cipher developed by the Korea Information Security Agency
+ (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`` bits in
@@ -275,6 +275,19 @@ 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:: CFB8(initialization_vector)
+
+ CFB (Cipher Feedback) is a mode of operation for block ciphers. It
+ transforms a block cipher into a stream cipher. The CFB8 variant uses an
+ 8-bit shift register.
+
+ **This mode does not require padding.**
+
+ :param bytes initialization_vector: Must be random bytes. 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``.
+
.. class:: GCM(initialization_vector, tag=None)
.. danger::
diff --git a/docs/installation.rst b/docs/installation.rst
index 3ebbecfd..8fbbcb30 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -24,6 +24,7 @@ We test compiling with ``clang`` as well as ``gcc`` and use the following
OpenSSL releases:
* ``OpenSSL 0.9.8e-fips-rhel5`` (``RHEL/CentOS 5``)
+* ``OpenSSL 0.9.8k``
* ``OpenSSL 0.9.8y``
* ``OpenSSL 1.0.0-fips`` (``RHEL/CentOS 6.4``)
* ``OpenSSL 1.0.1``
@@ -73,6 +74,7 @@ You should now be able to build and install cryptography with the usual
$ pip install cryptography
+
Using your own OpenSSL on Linux
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -106,7 +108,8 @@ Using your own OpenSSL on OS X
------------------------------
To link cryptography against a custom version of OpenSSL you'll need to set
-``ARCHFLAGS``, ``LDFLAGS``, and ``CFLAGS``. OpenSSL can be installed via `Homebrew`_ or `MacPorts`_:
+``ARCHFLAGS``, ``LDFLAGS``, and ``CFLAGS``. OpenSSL can be installed via
+`Homebrew`_ or `MacPorts`_:
`Homebrew`_
@@ -122,6 +125,31 @@ or `MacPorts`_:
$ sudo port install openssl
$ env ARCHFLAGS="-arch x86_64" LDFLAGS="-L/opt/local/lib" CFLAGS="-I/opt/local/include" pip install cryptography
+Building cryptography with conda
+--------------------------------
+
+Because of a `bug in conda`_, attempting to install cryptography out of the box
+will result in an error. This can be resolved by setting the library path
+environment variable for your platform.
+
+On OS X:
+
+.. code-block:: console
+
+ $ env DYLD_LIBRARY_PATH="$HOME/anaconda/lib" pip install cryptography
+
+and on Linux:
+
+.. code-block:: console
+
+ $ env LD_LIBRARY_PATH="$HOME/anaconda/lib" pip install cryptography
+
+You will need to set this variable every time you start Python. For more
+information, consult `Greg Wilson's blog post`_ on the subject.
+
+
.. _`Homebrew`: http://brew.sh
.. _`MacPorts`: http://www.macports.org
.. _`pre-compiled binaries`: https://www.openssl.org/related/binaries.html
+.. _`bug in conda`: https://github.com/conda/conda-recipes/issues/110
+.. _`Greg Wilson's blog post`: http://software-carpentry.org/blog/2014/04/mr-biczo-was-right.html
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index aae52ae7..9baf0822 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -1,3 +1,4 @@
+affine
backend
backends
Backends
@@ -8,6 +9,7 @@ Changelog
ciphertext
committer
committers
+conda
crypto
cryptographic
cryptographically
diff --git a/pytest.ini b/pytest.ini
index f717693d..9b44f198 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -9,4 +9,5 @@ markers =
pbkdf2hmac: this test requires a backend providing PBKDF2HMACBackend
rsa: this test requires a backend providing RSABackend
traditional_openssl_serialization: this test requires a backend providing TraditionalOpenSSLSerializationBackend
+ pkcs8_serialization: this test requires a backend providing PKCS8SerializationBackend
supported: parametrized test requiring only_if and skip_message
diff --git a/setup.py b/setup.py
index c841911f..4dd9e629 100644
--- a/setup.py
+++ b/setup.py
@@ -44,6 +44,7 @@ requirements = [
# If you add a new dep here you probably need to add it in the tox.ini as well
test_requirements = [
"pytest",
+ "pyasn1",
"pretend",
"iso8601",
]
diff --git a/tasks.py b/tasks.py
index c205ac88..9ffdc8a7 100644
--- a/tasks.py
+++ b/tasks.py
@@ -62,7 +62,7 @@ def download_artifacts():
response.raise_for_status()
for artifact in response.json()["artifacts"]:
response = requests.get(
- "{0}artifacts/{1}".format(run["url"], artifact["relativePath"])
+ "{0}artifact/{1}".format(run["url"], artifact["relativePath"])
)
out_path = os.path.join(
os.path.dirname(__file__),
diff --git a/tests/conftest.py b/tests/conftest.py
index 86d5a03b..b1326dc8 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -18,7 +18,8 @@ import pytest
from cryptography.hazmat.backends import _available_backends
from cryptography.hazmat.backends.interfaces import (
CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend,
- PBKDF2HMACBackend, RSABackend, TraditionalOpenSSLSerializationBackend
+ PBKDF2HMACBackend, PKCS8SerializationBackend, RSABackend,
+ TraditionalOpenSSLSerializationBackend
)
from .utils import check_backend_support, check_for_iface, select_backends
@@ -45,6 +46,11 @@ def pytest_runtest_setup(item):
TraditionalOpenSSLSerializationBackend,
item
)
+ check_for_iface(
+ "pkcs8_serialization",
+ PKCS8SerializationBackend,
+ item
+ )
check_backend_support(item)
diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py
index d8c09bd7..3fa364e2 100644
--- a/tests/hazmat/backends/test_multibackend.py
+++ b/tests/hazmat/backends/test_multibackend.py
@@ -98,6 +98,21 @@ class DummyRSABackend(object):
algorithm):
pass
+ def mgf1_hash_supported(self, algorithm):
+ pass
+
+ def rsa_padding_supported(self, padding):
+ pass
+
+ def generate_rsa_parameters_supported(self, public_exponent, key_size):
+ pass
+
+ def decrypt_rsa(self, private_key, ciphertext, padding):
+ pass
+
+ def encrypt_rsa(self, public_key, plaintext, padding):
+ pass
+
@utils.register_interface(DSABackend)
class DummyDSABackend(object):
@@ -107,6 +122,18 @@ class DummyDSABackend(object):
def generate_dsa_private_key(self, parameters):
pass
+ def create_dsa_signature_ctx(self, private_key, algorithm):
+ pass
+
+ def create_dsa_verification_ctx(self, public_key, signature, algorithm):
+ pass
+
+ def dsa_hash_supported(self, algorithm):
+ pass
+
+ def dsa_parameters_supported(self, p, q, g):
+ pass
+
@utils.register_interface(CMACBackend)
class DummyCMACBackend(object):
@@ -199,6 +226,16 @@ class TestMultiBackend(object):
backend.create_rsa_verification_ctx("public_key", "sig",
padding.PKCS1v15(), hashes.MD5())
+ backend.mgf1_hash_supported(hashes.MD5())
+
+ backend.rsa_padding_supported(padding.PKCS1v15())
+
+ backend.generate_rsa_parameters_supported(65537, 1024)
+
+ backend.encrypt_rsa("public_key", "encryptme", padding.PKCS1v15())
+
+ backend.decrypt_rsa("private_key", "encrypted", padding.PKCS1v15())
+
backend = MultiBackend([])
with raises_unsupported_algorithm(
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
@@ -217,6 +254,31 @@ class TestMultiBackend(object):
backend.create_rsa_verification_ctx(
"public_key", "sig", padding.PKCS1v15(), hashes.MD5())
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.mgf1_hash_supported(hashes.MD5())
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.rsa_padding_supported(padding.PKCS1v15())
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.generate_rsa_parameters_supported(65537, 1024)
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.encrypt_rsa("public_key", "encryptme", padding.PKCS1v15())
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.decrypt_rsa("private_key", "encrypted", padding.PKCS1v15())
+
def test_dsa(self):
backend = MultiBackend([
DummyDSABackend()
@@ -227,6 +289,11 @@ class TestMultiBackend(object):
parameters = object()
backend.generate_dsa_private_key(parameters)
+ backend.create_dsa_verification_ctx("public_key", "sig", hashes.SHA1())
+ backend.create_dsa_signature_ctx("private_key", hashes.SHA1())
+ backend.dsa_hash_supported(hashes.SHA1())
+ backend.dsa_parameters_supported(1, 2, 3)
+
backend = MultiBackend([])
with raises_unsupported_algorithm(
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
@@ -238,6 +305,28 @@ class TestMultiBackend(object):
):
backend.generate_dsa_private_key(parameters)
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.create_dsa_signature_ctx("private_key", hashes.SHA1())
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.create_dsa_verification_ctx(
+ "public_key", b"sig", hashes.SHA1()
+ )
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.dsa_hash_supported(hashes.SHA1())
+
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ backend.dsa_parameters_supported('p', 'q', 'g')
+
def test_cmac(self):
backend = MultiBackend([
DummyCMACBackend([algorithms.AES])
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index bba7d758..bfcdf14a 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -13,6 +13,8 @@
from __future__ import absolute_import, division, print_function
+import pretend
+
import pytest
from cryptography import utils
@@ -22,7 +24,7 @@ from cryptography.hazmat.primitives import hashes, interfaces
from cryptography.hazmat.primitives.asymmetric import dsa, padding, rsa
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
-from cryptography.hazmat.primitives.ciphers.modes import CBC
+from cryptography.hazmat.primitives.ciphers.modes import CBC, CTR
from cryptography.hazmat.primitives.interfaces import BlockCipherAlgorithm
from ...utils import raises_unsupported_algorithm
@@ -41,11 +43,20 @@ class DummyCipher(object):
name = "dummy-cipher"
+@utils.register_interface(interfaces.AsymmetricPadding)
+class DummyPadding(object):
+ name = "dummy-cipher"
+
+
@utils.register_interface(interfaces.HashAlgorithm)
class DummyHash(object):
name = "dummy-hash"
+class DummyMGF(object):
+ _salt_length = 0
+
+
class TestOpenSSL(object):
def test_backend_exists(self):
assert backend
@@ -64,6 +75,11 @@ class TestOpenSSL(object):
def test_supports_cipher(self):
assert backend.cipher_supported(None, None) is False
+ def test_aes_ctr_always_available(self):
+ # AES CTR should always be available in both 0.9.8 and 1.0.0+
+ assert backend.cipher_supported(AES(b"\x00" * 16),
+ CTR(b"\x00" * 16)) is True
+
def test_register_duplicate_cipher_adapter(self):
with pytest.raises(ValueError):
backend.register_cipher_adapter(AES, CBC, None)
@@ -144,15 +160,6 @@ class TestOpenSSL(object):
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH):
backend.derive_pbkdf2_hmac(hashes.SHA256(), 10, b"", 1000, b"")
- # This test is not in the TestOpenSSLRandomEngine class because to check
- # if it's really default we don't want to run the setup_method before it
- def test_osrandom_engine_is_default(self):
- e = backend._lib.ENGINE_get_default_RAND()
- name = backend._lib.ENGINE_get_name(e)
- assert name == backend._lib.Cryptography_osrandom_engine_name
- res = backend._lib.ENGINE_free(e)
- assert res == 1
-
@pytest.mark.skipif(
backend._lib.OPENSSL_VERSION_NUMBER >= 0x1000000f,
reason="Requires an older OpenSSL. Must be < 1.0.0"
@@ -174,6 +181,25 @@ class TestOpenSSL(object):
parameters = dsa.DSAParameters.generate(3072, backend)
assert utils.bit_length(parameters.p) == 3072
+ def test_int_to_bn(self):
+ value = (2 ** 4242) - 4242
+ bn = backend._int_to_bn(value)
+ assert bn != backend._ffi.NULL
+ bn = backend._ffi.gc(bn, backend._lib.BN_free)
+
+ assert bn
+ assert backend._bn_to_int(bn) == value
+
+ def test_int_to_bn_inplace(self):
+ value = (2 ** 4242) - 4242
+ bn_ptr = backend._lib.BN_new()
+ assert bn_ptr != backend._ffi.NULL
+ bn_ptr = backend._ffi.gc(bn_ptr, backend._lib.BN_free)
+ bn = backend._int_to_bn(value, bn_ptr)
+
+ assert bn == bn_ptr
+ assert backend._bn_to_int(bn_ptr) == value
+
class TestOpenSSLRandomEngine(object):
def teardown_method(self, method):
@@ -184,6 +210,15 @@ class TestOpenSSLRandomEngine(object):
name = backend._lib.ENGINE_get_name(current_default)
assert name == backend._lib.Cryptography_osrandom_engine_name
+ # This must be the first test in the class so that the teardown method
+ # has not (potentially) altered the default engine.
+ def test_osrandom_engine_is_default(self):
+ e = backend._lib.ENGINE_get_default_RAND()
+ name = backend._lib.ENGINE_get_name(e)
+ assert name == backend._lib.Cryptography_osrandom_engine_name
+ res = backend._lib.ENGINE_free(e)
+ assert res == 1
+
def test_osrandom_sanity_check(self):
# This test serves as a check against catastrophic failure.
buf = backend._ffi.new("char[]", 500)
@@ -234,27 +269,30 @@ class TestOpenSSLRandomEngine(object):
e = backend._lib.ENGINE_get_default_RAND()
assert e == backend._ffi.NULL
- def test_int_to_bn(self):
- value = (2 ** 4242) - 4242
- bn = backend._int_to_bn(value)
- assert bn != backend._ffi.NULL
- bn = backend._ffi.gc(bn, backend._lib.BN_free)
- assert bn
- assert backend._bn_to_int(bn) == value
+class TestOpenSSLRSA(object):
+ def test_generate_rsa_parameters_supported(self):
+ assert backend.generate_rsa_parameters_supported(1, 1024) is False
+ assert backend.generate_rsa_parameters_supported(4, 1024) is False
+ assert backend.generate_rsa_parameters_supported(3, 1024) is True
+ assert backend.generate_rsa_parameters_supported(3, 511) is False
- def test_int_to_bn_inplace(self):
- value = (2 ** 4242) - 4242
- bn_ptr = backend._lib.BN_new()
- assert bn_ptr != backend._ffi.NULL
- bn_ptr = backend._ffi.gc(bn_ptr, backend._lib.BN_free)
- bn = backend._int_to_bn(value, bn_ptr)
+ def test_generate_bad_public_exponent(self):
+ with pytest.raises(ValueError):
+ backend.generate_rsa_private_key(public_exponent=1, key_size=2048)
- assert bn == bn_ptr
- assert backend._bn_to_int(bn_ptr) == value
+ with pytest.raises(ValueError):
+ backend.generate_rsa_private_key(public_exponent=4, key_size=2048)
+ def test_cant_generate_insecure_tiny_key(self):
+ with pytest.raises(ValueError):
+ backend.generate_rsa_private_key(public_exponent=65537,
+ key_size=511)
+
+ with pytest.raises(ValueError):
+ backend.generate_rsa_private_key(public_exponent=65537,
+ key_size=256)
-class TestOpenSSLRSA(object):
@pytest.mark.skipif(
backend._lib.OPENSSL_VERSION_NUMBER >= 0x1000100f,
reason="Requires an older OpenSSL. Must be < 1.0.1"
@@ -270,8 +308,8 @@ class TestOpenSSLRSA(object):
padding.PSS(
mgf=padding.MGF1(
algorithm=hashes.SHA256(),
- salt_length=padding.MGF1.MAX_LENGTH
- )
+ ),
+ salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA1(),
backend
@@ -283,8 +321,8 @@ class TestOpenSSLRSA(object):
padding.PSS(
mgf=padding.MGF1(
algorithm=hashes.SHA256(),
- salt_length=padding.MGF1.MAX_LENGTH
- )
+ ),
+ salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA1(),
backend
@@ -293,6 +331,44 @@ class TestOpenSSLRSA(object):
def test_unsupported_mgf1_hash_algorithm(self):
assert backend.mgf1_hash_supported(DummyHash()) is False
+ def test_rsa_padding_unsupported_pss_mgf1_hash(self):
+ assert backend.rsa_padding_supported(
+ padding.PSS(mgf=padding.MGF1(DummyHash()), salt_length=0)
+ ) is False
+
+ def test_rsa_padding_unsupported(self):
+ assert backend.rsa_padding_supported(DummyPadding()) is False
+
+ def test_rsa_padding_supported_pkcs1v15(self):
+ assert backend.rsa_padding_supported(padding.PKCS1v15()) is True
+
+ def test_rsa_padding_supported_pss(self):
+ assert backend.rsa_padding_supported(
+ padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0)
+ ) is True
+
+ def test_rsa_padding_supported_oaep(self):
+ assert backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ algorithm=hashes.SHA1(),
+ label=None
+ ),
+ ) is True
+
+ def test_rsa_padding_unsupported_mgf(self):
+ assert backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=DummyMGF(),
+ algorithm=hashes.SHA1(),
+ label=None
+ ),
+ ) is False
+
+ assert backend.rsa_padding_supported(
+ padding.PSS(mgf=DummyMGF(), salt_length=0)
+ ) is False
+
def test_unsupported_mgf1_hash_algorithm_decrypt(self):
private_key = rsa.RSAPrivateKey.generate(
public_exponent=65537,
@@ -358,3 +434,14 @@ class TestOpenSSLCMAC(object):
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
backend.create_cmac_ctx(FakeAlgorithm())
+
+
+class TestOpenSSLSerialisationWithOpenSSL(object):
+ def test_password_too_long(self):
+ ffi_cb, cb = backend._pem_password_cb(b"aa")
+ assert cb(None, 1, False, None) == 0
+
+ def test_unsupported_evp_pkey_type(self):
+ key = pretend.stub(type="unsupported")
+ with raises_unsupported_algorithm(None):
+ backend._evp_pkey_to_private_key(key)
diff --git a/tests/hazmat/primitives/fixtures_rsa.py b/tests/hazmat/primitives/fixtures_rsa.py
new file mode 100644
index 00000000..494b9942
--- /dev/null
+++ b/tests/hazmat/primitives/fixtures_rsa.py
@@ -0,0 +1,493 @@
+# icensed under the Apache icense, Version 2.0 (the "icense");
+# you may not use this file except in compliance with the icense.
+# You may obtain a copy of the icense at
+#
+# http://www.apache.org/licenses/ICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the icense is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the icense for the specific language governing permissions and
+# limitations under the icense.
+
+
+from __future__ import absolute_import, division, print_function
+
+
+RSA_KEY_512 = {
+ "p": int(
+ "d57846898d5c0de249c08467586cb458fa9bc417cdf297f73cfc52281b787cd9", 16
+ ),
+ "q": int(
+ "d10f71229e87e010eb363db6a85fd07df72d985b73c42786191f2ce9134afb2d", 16
+ ),
+ "private_exponent": int(
+ "272869352cacf9c866c4e107acc95d4c608ca91460a93d28588d51cfccc07f449"
+ "18bbe7660f9f16adc2b4ed36ca310ef3d63b79bd447456e3505736a45a6ed21", 16
+ ),
+ "dmp1": int(
+ "addff2ec7564c6b64bc670d250b6f24b0b8db6b2810099813b7e7658cecf5c39", 16
+ ),
+ "dmq1": int(
+ "463ae9c6b77aedcac1397781e50e4afc060d4b216dc2778494ebe42a6850c81", 16
+ ),
+ "iqmp": int(
+ "54deef8548f65cad1d411527a32dcb8e712d3e128e4e0ff118663fae82a758f4", 16
+ ),
+ "public_exponent": 65537,
+ "modulus": int(
+ "ae5411f963c50e3267fafcf76381c8b1e5f7b741fdb2a544bcf48bd607b10c991"
+ "90caeb8011dc22cf83d921da55ec32bd05cac3ee02ca5e1dbef93952850b525", 16
+ ),
+}
+
+RSA_KEY_512_ALT = {
+ "p": int(
+ "febe19c29a0b50fefa4f7b1832f84df1caf9be8242da25c9d689e18226e67ce5",
+ 16),
+ "q": int(
+ "eb616c639dd999feda26517e1c77b6878f363fe828c4e6670ec1787f28b1e731",
+ 16),
+ "private_exponent": int(
+ "80edecfde704a806445a4cc782b85d3f36f17558f385654ea767f006470fdfcbda5e2"
+ "206839289d3f419b4e4fb8e1acee1b4fb9c591f69b64ec83937f5829241", 16),
+ "dmp1": int(
+ "7f4fa06e2a3077a54691cc5216bf13ad40a4b9fa3dd0ea4bca259487484baea5",
+ 16),
+ "dmq1": int(
+ "35eaa70d5a8711c352ed1c15ab27b0e3f46614d575214535ae279b166597fac1",
+ 16),
+ "iqmp": int(
+ "cc1f272de6846851ec80cb89a02dbac78f44b47bc08f53b67b4651a3acde8b19",
+ 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "ea397388b999ef0f7e7416fa000367efd9a0ba0deddd3f8160d1c36d62267f210fbd9"
+ "c97abeb6654450ff03e7601b8caa6c6f4cba18f0b52c179d17e8f258ad5", 16),
+}
+
+RSA_KEY_522 = {
+ "p": int(
+ "1a8aab9a069f92b52fdf05824f2846223dc27adfc806716a247a77d4c36885e4bf",
+ 16),
+ "q": int(
+ "19e8d620d177ec54cdb733bb1915e72ef644b1202b889ceb524613efa49c07eb4f",
+ 16),
+ "private_exponent": int(
+ "10b8a7c0a92c1ae2d678097d69db3bfa966b541fb857468291d48d1b52397ea2bac0d"
+ "4370c159015c7219e3806a01bbafaffdd46f86e3da1e2d1fe80a0369ccd745", 16),
+ "dmp1": int(
+ "3eb6277f66e6e2dcf89f1b8529431f730839dbd9a3e49555159bc8470eee886e5",
+ 16),
+ "dmq1": int(
+ "184b4d74aa54c361e51eb23fee4eae5e4786b37b11b6e0447af9c0b9c4e4953c5b",
+ 16),
+ "iqmp": int(
+ "f80e9ab4fa7b35d0d232ef51c4736d1f2dcf2c7b1dd8716211b1bf1337e74f8ae",
+ 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "2afaea0e0bb6fca037da7d190b5270a6c665bc18e7a456f7e69beaac4433db748ba99"
+ "acdd14697e453bca596eb35b47f2d48f1f85ef08ce5109dad557a9cf85ebf1", 16),
+}
+
+RSA_KEY_599 = {
+ "p": int(
+ "cf95d20be0c7af69f4b3d909f65d858c26d1a7ef34da8e3977f4fa230580e58814b54"
+ "24be99", 16),
+ "q": int(
+ "6052be4b28debd4265fe12ace5aa4a0c4eb8d63ff8853c66824b35622161eb48a3bc8"
+ "c3ada5", 16),
+ "private_exponent": int(
+ "69d9adc465e61585d3142d7cc8dd30605e8d1cbbf31009bc2cd5538dc40528d5d68ee"
+ "fe6a42d23674b6ec76e192351bf368c8968f0392110bf1c2825dbcff071270b80adcc"
+ "fa1d19d00a1", 16),
+ "dmp1": int(
+ "a86d10edde456687fba968b1f298d2e07226adb1221b2a466a93f3d83280f0bb46c20"
+ "2b6811", 16),
+ "dmq1": int(
+ "40d570e08611e6b1da94b95d46f8e7fe80be48f7a5ff8838375b08039514a399b11c2"
+ "80735", 16),
+ "iqmp": int(
+ "cd051cb0ea68b88765c041262ace2ec4db11dab14afd192742e34d5da3328637fabdf"
+ "bae26e", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "4e1b470fe00642426f3808e74c959632dd67855a4c503c5b7876ccf4dc7f6a1a49107"
+ "b90d26daf0a7879a6858218345fbc6e59f01cd095ca5647c27c25265e6c474fea8953"
+ "7191c7073d9d", 16),
+}
+
+RSA_KEY_745 = {
+ "p": int(
+ "1c5a0cfe9a86debd19eca33ba961f15bc598aa7983a545ce775b933afc89eb51bcf90"
+ "836257fdd060d4b383240241d", 16
+ ),
+ "q": int(
+ "fb2634f657f82ee6b70553382c4e2ed26b947c97ce2f0016f1b282cf2998184ad0527"
+ "a9eead826dd95fe06b57a025", 16
+ ),
+ "private_exponent": int(
+ "402f30f976bc07d15ff0779abff127b20a8b6b1d0024cc2ad8b6762d38f174f81e792"
+ "3b49d80bdbdd80d9675cbc7b2793ec199a0430eb5c84604dacfdb29259ae6a1a44676"
+ "22f0b23d4cb0f5cb1db4b8173c8d9d3e57a74dbd200d2141", 16),
+ "dmp1": int(
+ "e5e95b7751a6649f199be21bef7a51c9e49821d945b6fc5f538b4a670d8762c375b00"
+ "8e70f31d52b3ea2bd14c3101", 16),
+ "dmq1": int(
+ "12b85d5843645f72990fcf8d2f58408b34b3a3b9d9078dd527fceb5d2fb7839008092"
+ "dd4aca2a1fb00542801dcef5", 16),
+ "iqmp": int(
+ "5672740d947f621fc7969e3a44ec26736f3f819863d330e63e9409e139d20753551ac"
+ "c16544dd2bdadb9dee917440", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "1bd085f92237774d34013b477ceebbb2f2feca71118db9b7429341477947e7b1d04e8"
+ "c43ede3c52bb25781af58d4ff81289f301eac62dc3bcd7dafd7a4d5304e9f308e7669"
+ "52fbf2b62373e66611fa53189987dbef9f7243dcbbeb25831", 16),
+}
+
+RSA_KEY_768 = {
+ "p": int(
+ "f80c0061b607f93206b68e208906498d68c6e396faf457150cf975c8f849848465869"
+ "7ecd402313397088044c4c2071b", 16),
+ "q": int(
+ "e5b5dbecc93c6d306fc14e6aa9737f9be2728bc1a326a8713d2849b34c1cb54c63468"
+ "3a68abb1d345dbf15a3c492cf55", 16),
+ "private_exponent": int(
+ "d44601442255ffa331212c60385b5e898555c75c0272632ff42d57c4b16ca97dbca9f"
+ "d6d99cd2c9fd298df155ed5141b4be06c651934076133331d4564d73faed7ce98e283"
+ "2f7ce3949bc183be7e7ca34f6dd04a9098b6c73649394b0a76c541", 16),
+ "dmp1": int(
+ "a5763406fa0b65929661ce7b2b8c73220e43a5ebbfe99ff15ddf464fd238105ad4f2a"
+ "c83818518d70627d8908703bb03", 16),
+ "dmq1": int(
+ "cb467a9ef899a39a685aecd4d0ad27b0bfdc53b68075363c373d8eb2bed8eccaf3533"
+ "42f4db735a9e087b7539c21ba9d", 16),
+ "iqmp": int(
+ "5fe86bd3aee0c4d09ef11e0530a78a4534c9b833422813b5c934a450c8e564d8097a0"
+ "6fd74f1ebe2d5573782093f587a", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "de92f1eb5f4abf426b6cac9dd1e9bf57132a4988b4ed3f8aecc15e251028bd6df46eb"
+ "97c711624af7db15e6430894d1b640c13929329241ee094f5a4fe1a20bc9b75232320"
+ "a72bc567207ec54d6b48dccb19737cf63acc1021abb337f19130f7", 16),
+}
+
+RSA_KEY_1024 = {
+ "p": int(
+ "ea4d9d9a1a068be44b9a5f8f6de0512b2c5ba1fb804a4655babba688e6e890b347c1a"
+ "7426685a929337f513ae4256f0b7e5022d642237f960c5b24b96bee8e51", 16),
+ "q": int(
+ "cffb33e400d6f08b410d69deb18a85cf0ed88fcca9f32d6f2f66c62143d49aff92c11"
+ "4de937d4f1f62d4635ee89af99ce86d38a2b05310f3857c7b5d586ac8f9", 16),
+ "private_exponent": int(
+ "3d12d46d04ce942fb99be7bf30587b8cd3e21d75a2720e7bda1b867f1d418d91d8b9f"
+ "e1c00181fdde94f2faf33b4e6f800a1b3ae3b972ccb6d5079dcb6c794070ac8306d59"
+ "c00b58b7a9a81122a6b055832de7c72334a07494d8e7c9fbeed2cc37e011d9e6bfc6e"
+ "9bcddbef7f0f5771d9cf82cd4b268c97ec684575c24b6c881", 16),
+ "dmp1": int(
+ "470f2b11257b7ec9ca34136f487f939e6861920ad8a9ae132a02e74af5dceaa5b4c98"
+ "2949ccb44b67e2bcad2f58674db237fe250e0d62b47b28fa1dfaa603b41", 16),
+ "dmq1": int(
+ "c616e8317d6b3ae8272973709b80e8397256697ff14ea03389de454f619f99915a617"
+ "45319fefbe154ec1d49441a772c2f63f7d15c478199afc60469bfd0d561", 16),
+ "iqmp": int(
+ "d15e7c9ad357dfcd5dbdc8427680daf1006761bcfba93a7f86589ad88832a8d564b1c"
+ "d4291a658c96fbaea7ca588795820902d85caebd49c2d731e3fe0243130", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "be5aac07456d990133ebce69c06b48845b972ab1ad9f134bc5683c6b5489b5119ede0"
+ "7be3bed0e355d48e0dfab1e4fb5187adf42d7d3fb0401c082acb8481bf17f0e871f88"
+ "77be04c3a1197d40aa260e2e0c48ed3fd2b93dc3fc0867591f67f3cd60a77adee1d68"
+ "a8c3730a5702485f6ac9ede7f0fd2918e037ee4cc1fc1b4c9", 16),
+}
+
+RSA_KEY_1025 = {
+ "p": int(
+ "18e9bfb7071725da04d31c103fa3563648c69def43a204989214eb57b0c8b299f9ef3"
+ "5dda79a62d8d67fd2a9b69fbd8d0490aa2edc1e111a2b8eb7c737bb691a5", 16),
+ "q": int(
+ "d8eccaeeb95815f3079d13685f3f72ca2bf2550b349518049421375df88ca9bbb4ba8"
+ "cb0e3502203c9eeae174112509153445d251313e4711a102818c66fcbb7", 16),
+ "private_exponent": int(
+ "fe9ac54910b8b1bc948a03511c54cab206a1d36d50d591124109a48abb7480977ccb0"
+ "47b4d4f1ce7b0805df2d4fa3fe425f49b78535a11f4b87a4eba0638b3340c23d4e6b2"
+ "1ecebe9d5364ea6ead2d47b27836019e6ecb407000a50dc95a8614c9d0031a6e3a524"
+ "d2345cfb76e15c1f69d5ba35bdfb6ec63bcb115a757ef79d9", 16),
+ "dmp1": int(
+ "18537e81006a68ea76d590cc88e73bd26bc38d09c977959748e5265c0ce21c0b5fd26"
+ "53d975f97ef759b809f791487a8fff1264bf561627fb4527a3f0bbb72c85", 16),
+ "dmq1": int(
+ "c807eac5a1f1e1239f04b04dd16eff9a00565127a91046fa89e1eb5d6301cace85447"
+ "4d1f47b0332bd35b4214b66e9166953241538f761f30d969272ee214f17", 16),
+ "iqmp": int(
+ "133aa74dd41fe70fa244f07d0c4091a22f8c8f0134fe6aea9ec8b55383b758fefe358"
+ "2beec36eca91715eee7d21931f24fa9e97e8e3a50f9cd0f731574a5eafcc", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "151c44fed756370fb2d4a0e6ec7dcac84068ca459b6aaf22daf902dca72c77563bf27"
+ "6fe3523f38f5ddaf3ea9aa88486a9d8760ff732489075862bee0e599de5c5f509b451"
+ "9f4f446521bad15cd279a498fe1e89107ce0d237e3103d7c5eb80166642e2924b152a"
+ "ebff97b71fdd2d68ebb45034cc784e2e822ff6d1edf98af3f3", 16),
+}
+
+RSA_KEY_1026 = {
+ "p": int(
+ "1fcbfb8719c5bdb5fe3eb0937c76bb096e750b9442dfe31d6a877a13aed2a6a4e9f79"
+ "40f815f1c307dd6bc2b4b207bb6fe5be3a15bd2875a957492ce197cdedb1", 16),
+ "q": int(
+ "1f704a0f6b8966dd52582fdc08227dd3dbaeaa781918b41144b692711091b4ca4eb62"
+ "985c3513853828ce8739001dfba9a9a7f1a23cbcaf74280be925e2e7b50d", 16),
+ "private_exponent": int(
+ "c67975e35a1d0d0b3ebfca736262cf91990cb31cf4ac473c0c816f3bc2720bcba2475"
+ "e8d0de8535d257816c0fc53afc1b597eada8b229069d6ef2792fc23f59ffb4dc6c3d9"
+ "0a3c462082025a4cba7561296dd3d8870c4440d779406f00879afe2c681e7f5ee055e"
+ "ff829e6e55883ec20830c72300762e6e3a333d94b4dbe4501", 16),
+ "dmp1": int(
+ "314730ca7066c55d086a9fbdf3670ef7cef816b9efea8b514b882ae9d647217cf41d7"
+ "e9989269dc9893d02e315cb81f058c49043c2cac47adea58bdf5e20e841", 16),
+ "dmq1": int(
+ "1da28a9d687ff7cfeebc2439240de7505a8796376968c8ec723a2b669af8ce53d9c88"
+ "af18540bd78b2da429014923fa435f22697ac60812d7ca9c17a557f394cd", 16),
+ "iqmp": int(
+ "727947b57b8a36acd85180522f1b381bce5fdbd962743b3b14af98a36771a80f58ddd"
+ "62675d72a5935190da9ddc6fd6d6d5e9e9f805a2e92ab8d56b820493cdf", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "3e7a5e6483e55eb8b723f9c46732d21b0af9e06a4a1099962d67a35ee3f62e3129cfa"
+ "e6ab0446da18e26f33e1d753bc1cc03585c100cf0ab5ef056695706fc8b0c9c710cd7"
+ "3fe6e5beda70f515a96fabd3cc5ac49efcb2594b220ff3b603fcd927f6a0838ef04bf"
+ "52f3ed9eab801f09e5aed1613ddeb946ed0fbb02060b3a36fd", 16),
+}
+
+RSA_KEY_1027 = {
+ "p": int(
+ "30135e54cfb072c3d3eaf2000f3ed92ceafc85efc867b9d4bf5612f2978c432040093"
+ "4829f741c0f002b54af2a4433ff872b6321ef00ff1e72cba4e0ced937c7d", 16),
+ "q": int(
+ "1d01a8aead6f86b78c875f18edd74214e06535d65da054aeb8e1851d6f3319b4fb6d8"
+ "6b01e07d19f8261a1ded7dc08116345509ab9790e3f13e65c037e5bb7e27", 16),
+ "private_exponent": int(
+ "21cf4477df79561c7818731da9b9c88cd793f1b4b8e175bd0bfb9c0941a4dc648ecf1"
+ "6d96b35166c9ea116f4c2eb33ce1c231e641a37c25e54c17027bdec08ddafcb83642e"
+ "795a0dd133155ccc5eed03b6e745930d9ac7cfe91f9045149f33295af03a2198c660f"
+ "08d8150d13ce0e2eb02f21ac75d63b55822f77bd5be8d07619", 16),
+ "dmp1": int(
+ "173fb695931e845179511c18b546b265cb79b517c135902377281bdf9f34205e1f399"
+ "4603ad63e9f6e7885ea73a929f03fa0d6bed943051ce76cddde2d89d434d", 16),
+ "dmq1": int(
+ "10956b387b2621327da0c3c8ffea2af8be967ee25163222746c28115a406e632a7f12"
+ "5a9397224f1fa5c116cd3a313e5c508d31db2deb83b6e082d213e33f7fcf", 16),
+ "iqmp": int(
+ "234f833949f2c0d797bc6a0e906331e17394fa8fbc8449395766d3a8d222cf6167c48"
+ "8e7fe1fe9721d3e3b699a595c8e6f063d92bd840dbc84d763b2b37002109", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "57281707d7f9b1369c117911758980e32c05b133ac52c225bcf68b79157ff47ea0a5a"
+ "e9f579ef1fd7e42937f921eb3123c4a045cc47a2159fbbf904783e654954c42294c30"
+ "a95c15db7c7b91f136244e548f62474b137087346c5522e54f226f49d6c93bc58cb39"
+ "972e41bde452bb3ae9d60eb93e5e1ce91d222138d9890c7d0b", 16),
+}
+
+RSA_KEY_1028 = {
+ "p": int(
+ "359d17378fae8e9160097daee78a206bd52efe1b757c12a6da8026cc4fc4bb2620f12"
+ "b8254f4db6aed8228be8ee3e5a27ec7d31048602f01edb00befd209e8c75", 16),
+ "q": int(
+ "33a2e70b93d397c46e63b273dcd3dcfa64291342a6ce896e1ec8f1c0edc44106550f3"
+ "c06e7d3ca6ea29eccf3f6ab5ac6235c265313d6ea8e8767e6a343f616581", 16),
+ "private_exponent": int(
+ "880640088d331aa5c0f4cf2887809a420a2bc086e671e6ffe4e47a8c80792c038a314"
+ "9a8e45ef9a72816ab45b36e3af6800351067a6b2751843d4232413146bb575491463a"
+ "8addd06ce3d1bcf7028ec6c5d938c545a20f0a40214b5c574ca7e840062b2b5f8ed49"
+ "4b144bb2113677c4b10519177fee1d4f5fb8a1c159b0b47c01", 16),
+ "dmp1": int(
+ "75f8c52dad2c1cea26b8bba63236ee4059489e3d2db766136098bcc6b67fde8f77cd3"
+ "640035107bfb1ffc6480983cfb84fe0c3be008424ebc968a7db7e01f005", 16),
+ "dmq1": int(
+ "3893c59469e4ede5cd0e6ff9837ca023ba9b46ff40c60ccf1bec10f7d38db5b1ba817"
+ "6c41a3f750ec4203b711455aca06d1e0adffc5cffa42bb92c7cb77a6c01", 16),
+ "iqmp": int(
+ "ad32aafae3c962ac25459856dc8ef1f733c3df697eced29773677f435d186cf759d1a"
+ "5563dd421ec47b4d7e7f12f29647c615166d9c43fc49001b29089344f65", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "ad0696bef71597eb3a88e135d83c596930cac73868fbd7e6b2d64f34eea5c28cce351"
+ "0c68073954d3ba4deb38643e7a820a4cf06e75f7f82eca545d412bd63781945c28d40"
+ "6e95a6cced5ae924a8bfa4f3def3e0250d91246c269ec40c89c93a85acd3770ba4d2e"
+ "774732f43abe94394de43fb57f93ca25f7a59d75d400a3eff5", 16),
+}
+
+RSA_KEY_1029 = {
+ "p": int(
+ "66f33e513c0b6b6adbf041d037d9b1f0ebf8de52812a3ac397a963d3f71ba64b3ad04"
+ "e4d4b5e377e6fa22febcac292c907dc8dcfe64c807fd9a7e3a698850d983", 16),
+ "q": int(
+ "3b47a89a19022461dcc2d3c05b501ee76955e8ce3cf821beb4afa85a21a26fd7203db"
+ "deb8941f1c60ada39fd6799f6c07eb8554113f1020460ec40e93cd5f6b21", 16),
+ "private_exponent": int(
+ "280c42af8b1c719821f2f6e2bf5f3dd53c81b1f3e1e7cc4fce6e2f830132da0665bde"
+ "bc1e307106b112b52ad5754867dddd028116cf4471bc14a58696b99524b1ad8f05b31"
+ "cf47256e54ab4399b6a073b2c0452441438dfddf47f3334c13c5ec86ece4d33409056"
+ "139328fafa992fb5f5156f25f9b21d3e1c37f156d963d97e41", 16),
+ "dmp1": int(
+ "198c7402a4ec10944c50ab8488d7b5991c767e75eb2817bd427dff10335ae141fa2e8"
+ "7c016dc22d975cac229b9ffdf7d943ddfd3a04b8bf82e83c3b32c5698b11", 16),
+ "dmq1": int(
+ "15fd30c7687b68ef7c2a30cdeb913ec56c4757c218cf9a04d995470797ee5f3a17558"
+ "fbb6d00af245d2631d893b382da48a72bc8a613024289895952ab245b0c1", 16),
+ "iqmp": int(
+ "4f8fde17e84557a3f4e242d889e898545ab55a1a8e075c9bb0220173ccffe84659abe"
+ "a235104f82e32750309389d4a52af57dbb6e48d831917b6efeb190176570", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "17d6e0a09aa5b2d003e51f43b9c37ffde74688f5e3b709fd02ef375cb6b8d15e299a9"
+ "f74981c3eeaaf947d5c2d64a1a80f5c5108a49a715c3f7be95a016b8d3300965ead4a"
+ "4df76e642d761526803e9434d4ec61b10cb50526d4dcaef02593085ded8c331c1b27b"
+ "200a45628403065efcb2c0a0ca1f75d648d40a007fbfbf2cae3", 16),
+}
+
+RSA_KEY_1030 = {
+ "p": int(
+ "6f4ac8a8172ef1154cf7f80b5e91de723c35a4c512860bfdbafcc3b994a2384bf7796"
+ "3a2dd0480c7e04d5d418629651a0de8979add6f47b23da14c27a682b69c9", 16),
+ "q": int(
+ "65a9f83e07dea5b633e036a9dccfb32c46bf53c81040a19c574c3680838fc6d28bde9"
+ "55c0ff18b30481d4ab52a9f5e9f835459b1348bbb563ad90b15a682fadb3", 16),
+ "private_exponent": int(
+ "290db707b3e1a96445ae8ea93af55a9f211a54ebe52995c2eb28085d1e3f09c986e73"
+ "a00010c8e4785786eaaa5c85b98444bd93b585d0c24363ccc22c482e150a3fd900176"
+ "86968e4fa20423ae72823b0049defceccb39bb34aa4ef64e6b14463b76d6a871c859e"
+ "37285455b94b8e1527d1525b1682ac6f7c8fd79d576c55318c1", 16),
+ "dmp1": int(
+ "23f7fa84010225dea98297032dac5d45745a2e07976605681acfe87e0920a8ab3caf5"
+ "9d9602f3d63dc0584f75161fd8fff20c626c21c5e02a85282276a74628a9", 16),
+ "dmq1": int(
+ "18ebb657765464a8aa44bf019a882b72a2110a77934c54915f70e6375088b10331982"
+ "962bce1c7edd8ef9d3d95aa2566d2a99da6ebab890b95375919408d00f33", 16),
+ "iqmp": int(
+ "3d59d208743c74054151002d77dcdfc55af3d41357e89af88d7eef2767be54c290255"
+ "9258d85cf2a1083c035a33e65a1ca46dc8b706847c1c6434cef7b71a9dae", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "2c326574320818a6a8cb6b3328e2d6c1ba2a3f09b6eb2bc543c03ab18eb5efdaa8fcd"
+ "bb6b4e12168304f587999f9d96a421fc80cb933a490df85d25883e6a88750d6bd8b3d"
+ "4117251eee8f45e70e6daac7dbbd92a9103c623a09355cf00e3f16168e38b9c4cb5b3"
+ "68deabbed8df466bc6835eaba959bc1c2f4ec32a09840becc8b", 16),
+}
+
+RSA_KEY_1031 = {
+ "p": int(
+ "c0958c08e50137db989fb7cc93abf1984543e2f955d4f43fb2967f40105e79274c852"
+ "293fa06ce63ca8436155e475ed6d1f73fea4c8e2516cc79153e3dc83e897", 16),
+ "q": int(
+ "78cae354ea5d6862e5d71d20273b7cddb8cdfab25478fe865180676b04250685c4d03"
+ "30c216574f7876a7b12dfe69f1661d3b0cea6c2c0dcfb84050f817afc28d", 16),
+ "private_exponent": int(
+ "1d55cc02b17a5d25bfb39f2bc58389004d0d7255051507f75ef347cdf5519d1a00f4b"
+ "d235ce4171bfab7bdb7a6dcfae1cf41433fb7da5923cc84f15a675c0b83492c95dd99"
+ "a9fc157aea352ffdcbb5d59dbc3662171d5838d69f130678ee27841a79ef64f679ce9"
+ "3821fa69c03f502244c04b737edad8967def8022a144feaab29", 16),
+ "dmp1": int(
+ "5b1c2504ec3a984f86b4414342b5bcf59a0754f13adf25b2a0edbc43f5ba8c3cc061d"
+ "80b03e5866d059968f0d10a98deaeb4f7830436d76b22cf41f2914e13eff", 16),
+ "dmq1": int(
+ "6c361e1819691ab5d67fb2a8f65c958d301cdf24d90617c68ec7005edfb4a7b638cde"
+ "79d4b61cfba5c86e8c0ccf296bc7f611cb8d4ae0e072a0f68552ec2d5995", 16),
+ "iqmp": int(
+ "b7d61945fdc8b92e075b15554bab507fa8a18edd0a18da373ec6c766c71eece61136a"
+ "84b90b6d01741d40458bfad17a9bee9d4a8ed2f6e270782dc3bf5d58b56e", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "5adebaa926ea11fb635879487fdd53dcfbb391a11ac7279bb3b4877c9b811370a9f73"
+ "da0690581691626d8a7cf5d972cced9c2091ccf999024b23b4e6dc6d99f80a454737d"
+ "ec0caffaebe4a3fac250ed02079267c8f39620b5ae3e125ca35338522dc9353ecac19"
+ "cb2fe3b9e3a9291619dbb1ea3a7c388e9ee6469fbf5fb22892b", 16),
+}
+
+RSA_KEY_1536 = {
+ "p": int(
+ "f1a65fa4e2aa6e7e2b560251e8a4cd65b625ad9f04f6571785782d1c213d91c961637"
+ "0c572f2783caf2899f7fb690cf99a0184257fbd4b071b212c88fb348279a5387e61f1"
+ "17e9c62980c45ea863fa9292087c0f66ecdcde6443d5a37268bf71", 16),
+ "q": int(
+ "e54c2cbc3839b1da6ae6fea45038d986d6f523a3ae76051ba20583aab711ea5965cf5"
+ "3cf54128cc9573f7460bba0fd6758a57aaf240c391790fb38ab473d83ef735510c53d"
+ "1d10c31782e8fd7da42615e33565745c30a5e6ceb2a3ae0666cc35", 16),
+ "private_exponent": int(
+ "7bcad87e23da2cb2a8c328883fabce06e1f8e9b776c8bf253ad9884e6200e3bd9bd3b"
+ "a2cbe87d3854527bf005ba5d878c5b0fa20cfb0a2a42884ae95ca12bf7304285e9214"
+ "5e992f7006c7c0ae839ad550da495b143bec0f4806c7f44caed45f3ccc6dc44cfaf30"
+ "7abdb757e3d28e41c2d21366835c0a41e50a95af490ac03af061d2feb36ac0afb87be"
+ "a13fb0f0c5a410727ebedb286c77f9469473fae27ef2c836da6071ef7efc1647f1233"
+ "4009a89eecb09a8287abc8c2afd1ddd9a1b0641", 16),
+ "dmp1": int(
+ "a845366cd6f9df1f34861bef7594ed025aa83a12759e245f58adaa9bdff9c3befb760"
+ "75d3701e90038e888eec9bf092df63400152cb25fc07effc6c74c45f0654ccbde15cd"
+ "90dd5504298a946fa5cf22a956072da27a6602e6c6e5c97f2db9c1", 16),
+ "dmq1": int(
+ "28b0c1e78cdac03310717992d321a3888830ec6829978c048156152d805b4f8919c61"
+ "70b5dd204e5ddf3c6c53bc6aff15d0bd09faff7f351b94abb9db980b31f150a6d7573"
+ "08eb66938f89a5225cb4dd817a824c89e7a0293b58fc2eefb7e259", 16),
+ "iqmp": int(
+ "6c1536c0e16e42a094b6caaf50231ba81916871497d73dcbbbd4bdeb9e60cae0413b3"
+ "8143b5d680275b29ed7769fe5577e4f9b3647ddb064941120914526d64d80016d2eb7"
+ "dc362da7c569623157f3d7cff8347f11494bf5c048d77e28d3f515", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "d871bb2d27672e54fc62c4680148cbdf848438da804e2c48b5a9c9f9daf6cc6e8ea7d"
+ "2296f25064537a9a542aef3dd449ea75774238d4da02c353d1bee70013dccc248ceef"
+ "4050160705c188043c8559bf6dbfb6c4bb382eda4e9547575a8227d5b3c0a70883913"
+ "64cf9f018d8bea053b226ec65e8cdbeaf48a071d0074860a734b1cb7d2146d43014b2"
+ "0776dea42f7853a54690e6cbbf3331a9f43763cfe2a51c3293bea3b2eebec0d8e43eb"
+ "317a443afe541107d886e5243c096091543ae65", 16),
+}
+
+RSA_KEY_2048 = {
+ "p": int(
+ "e14202e58c5f7446648d75e5dc465781f661f6b73000c080368afcfb21377f4ef19da"
+ "845d4ef9bc6b151f6d9f34629103f2e57615f9ba0a3a2fbb035069e1d63b4bb0e78ad"
+ "dad1ec3c6f87e25c877a1c4c1972098e09158ef7b9bc163852a18d44a70b7b31a03dc"
+ "2614fd9ab7bf002cba79054544af3bfbdb6aed06c7b24e6ab", 16),
+ "q": int(
+ "dbe2bea1ff92599bd19f9d045d6ce62250c05cfeac5117f3cf3e626cb696e3d886379"
+ "557d5a57b7476f9cf886accfd40508a805fe3b45a78e1a8a125e516cda91640ee6398"
+ "ec5a39d3e6b177ef12ab00d07907a17640e4ca454fd8487da3c4ffa0d5c2a5edb1221"
+ "1c8e33c7ee9fa6753771fd111ec04b8317f86693eb2928c89", 16),
+ "private_exponent": int(
+ "aef17f80f2653bc30539f26dd4c82ed6abc1d1b53bc0abcdbee47e9a8ab433abde865"
+ "9fcfae1244d22de6ad333c95aee7d47f30b6815065ac3322744d3ea75058002cd1b29"
+ "3141ee2a6dc682342432707080071bd2131d6262cab07871c28aa5238b87173fb78c3"
+ "7f9c7bcd18c12e8971bb77fd9fa3e0792fec18d8d9bed0b03ba02b263606f24dbace1"
+ "c8263ce2802a769a090e993fd49abc50c3d3c78c29bee2de0c98055d2f102f1c5684b"
+ "8dddee611d5205392d8e8dd61a15bf44680972a87f040a611a149271eeb2573f8bf6f"
+ "627dfa70e77def2ee6584914fa0290e041349ea0999cdff3e493365885b906cbcf195"
+ "843345809a85098cca90fea014a21", 16),
+ "dmp1": int(
+ "9ba56522ffcfa5244eae805c87cc0303461f82be29691b9a7c15a5a050df6c143c575"
+ "7c288d3d7ab7f32c782e9d9fcddc10a604e6425c0e5d0e46069035d95a923646d276d"
+ "d9d95b8696fa29ab0de18e53f6f119310f8dd9efca62f0679291166fed8cbd5f18fe1"
+ "3a5f1ead1d71d8c90f40382818c18c8d069be793dbc094f69", 16),
+ "dmq1": int(
+ "a8d4a0aaa2212ccc875796a81353da1fdf00d46676c88d2b96a4bfcdd924622d8e607"
+ "f3ac1c01dda7ebfb0a97dd7875c2a7b2db6728fb827b89c519f5716fb3228f4121647"
+ "04b30253c17de2289e9cce3343baa82eb404f789e094a094577a9b0c5314f1725fdf5"
+ "8e87611ad20da331bd30b8aebc7dc97d0e9a9ba8579772c9", 16),
+ "iqmp": int(
+ "17bd5ef638c49440d1853acb3fa63a5aca28cb7f94ed350db7001c8445da8943866a7"
+ "0936e1ee2716c98b484e357cc054d82fbbd98d42f880695d38a1dd4eb096f629b9417"
+ "aca47e6de5da9f34e60e8a0ffd7e35be74deeef67298d94b3e0db73fc4b7a4cb360c8"
+ "9d2117a0bfd9434d37dc7c027d6b01e5295c875015510917d", 16),
+ "public_exponent": 65537,
+ "modulus": int(
+ "c17afc7e77474caa5aa83036158a3ffbf7b5216851ba2230e5d6abfcc1c6cfef59e92"
+ "3ea1330bc593b73802ab608a6e4a3306523a3116ba5aa3966145174e13b6c49e9b780"
+ "62e449d72efb10fd49e91fa08b96d051e782e9f5abc5b5a6f7984827adb8e73da00f2"
+ "2b2efdcdb76eab46edad98ed65662743fdc6c0e336a5d0cdbaa7dc29e53635e24c87a"
+ "5b2c4215968063cdeb68a972babbc1e3cff00fb9a80e372a4d0c2c920d1e8cee333ce"
+ "470dc2e8145adb05bf29aee1d24f141e8cc784989c587fc6fbacd979f3f2163c1d729"
+ "9b365bc72ffe2848e967aed1e48dcc515b3a50ed4de04fd053846ca10a223b10cc841"
+ "cc80fdebee44f3114c13e886af583", 16),
+}
diff --git a/tests/hazmat/primitives/test_3des.py b/tests/hazmat/primitives/test_3des.py
index a4d696c9..b9354f0e 100644
--- a/tests/hazmat/primitives/test_3des.py
+++ b/tests/hazmat/primitives/test_3des.py
@@ -137,3 +137,40 @@ class TestTripleDESModeCFB(object):
),
lambda iv, **kwargs: modes.CFB(binascii.unhexlify(iv)),
)
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
+ algorithms.TripleDES("\x00" * 8), modes.CFB8("\x00" * 8)
+ ),
+ skip_message="Does not support TripleDES CFB8",
+)
+@pytest.mark.cipher
+class TestTripleDESModeCFB8(object):
+ test_KAT = generate_encrypt_test(
+ load_nist_vectors,
+ os.path.join("ciphers", "3DES", "CFB"),
+ [
+ "TCFB8invperm.rsp",
+ "TCFB8permop.rsp",
+ "TCFB8subtab.rsp",
+ "TCFB8varkey.rsp",
+ "TCFB8vartext.rsp",
+ ],
+ lambda keys, **kwargs: algorithms.TripleDES(binascii.unhexlify(keys)),
+ lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
+ )
+
+ test_MMT = generate_encrypt_test(
+ load_nist_vectors,
+ os.path.join("ciphers", "3DES", "CFB"),
+ [
+ "TCFB8MMT1.rsp",
+ "TCFB8MMT2.rsp",
+ "TCFB8MMT3.rsp",
+ ],
+ lambda key1, key2, key3, **kwargs: algorithms.TripleDES(
+ binascii.unhexlify(key1 + key2 + key3)
+ ),
+ lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
+ )
diff --git a/tests/hazmat/primitives/test_aes.py b/tests/hazmat/primitives/test_aes.py
index 03be268d..173075d6 100644
--- a/tests/hazmat/primitives/test_aes.py
+++ b/tests/hazmat/primitives/test_aes.py
@@ -158,6 +158,39 @@ class TestAESModeCFB(object):
@pytest.mark.supported(
only_if=lambda backend: backend.cipher_supported(
+ algorithms.AES("\x00" * 16), modes.CFB8("\x00" * 16)
+ ),
+ skip_message="Does not support AES CFB8",
+)
+@pytest.mark.cipher
+class TestAESModeCFB8(object):
+ test_CFB8 = generate_encrypt_test(
+ load_nist_vectors,
+ os.path.join("ciphers", "AES", "CFB"),
+ [
+ "CFB8GFSbox128.rsp",
+ "CFB8GFSbox192.rsp",
+ "CFB8GFSbox256.rsp",
+ "CFB8KeySbox128.rsp",
+ "CFB8KeySbox192.rsp",
+ "CFB8KeySbox256.rsp",
+ "CFB8VarKey128.rsp",
+ "CFB8VarKey192.rsp",
+ "CFB8VarKey256.rsp",
+ "CFB8VarTxt128.rsp",
+ "CFB8VarTxt192.rsp",
+ "CFB8VarTxt256.rsp",
+ "CFB8MMT128.rsp",
+ "CFB8MMT192.rsp",
+ "CFB8MMT256.rsp",
+ ],
+ lambda key, **kwargs: algorithms.AES(binascii.unhexlify(key)),
+ lambda iv, **kwargs: modes.CFB8(binascii.unhexlify(iv)),
+ )
+
+
+@pytest.mark.supported(
+ only_if=lambda backend: backend.cipher_supported(
algorithms.AES("\x00" * 16), modes.CTR("\x00" * 16)
),
skip_message="Does not support AES CTR",
diff --git a/tests/hazmat/primitives/test_block.py b/tests/hazmat/primitives/test_block.py
index acfd947c..022e3af7 100644
--- a/tests/hazmat/primitives/test_block.py
+++ b/tests/hazmat/primitives/test_block.py
@@ -184,6 +184,14 @@ class TestModeValidation(object):
backend,
)
+ def test_cfb8(self, backend):
+ with pytest.raises(ValueError):
+ Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.CFB8(b"abc"),
+ backend,
+ )
+
def test_ctr(self, backend):
with pytest.raises(ValueError):
Cipher(
diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py
index bc3b1db6..cbe10e9c 100644
--- a/tests/hazmat/primitives/test_dsa.py
+++ b/tests/hazmat/primitives/test_dsa.py
@@ -18,12 +18,15 @@ import os
import pytest
-from cryptography.exceptions import _Reasons
+from cryptography.exceptions import (
+ AlreadyFinalized, InvalidSignature, _Reasons)
+from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import dsa
from cryptography.utils import bit_length
from ...utils import (
- load_fips_dsa_key_pair_vectors, load_vectors_from_file,
+ der_encode_dsa_signature, load_fips_dsa_key_pair_vectors,
+ load_fips_dsa_sig_vectors, load_vectors_from_file,
raises_unsupported_algorithm
)
@@ -720,12 +723,152 @@ class TestDSA(object):
)
+@pytest.mark.dsa
+class TestDSAVerification(object):
+ _algorithms_dict = {
+ 'SHA1': hashes.SHA1,
+ 'SHA224': hashes.SHA224,
+ 'SHA256': hashes.SHA256,
+ 'SHA384': hashes.SHA384,
+ 'SHA512': hashes.SHA512
+ }
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "DSA", "FIPS_186-3", "SigVer.rsp"),
+ load_fips_dsa_sig_vectors
+ )
+ )
+ def test_dsa_verification(self, vector, backend):
+ digest_algorithm = vector['digest_algorithm'].replace("-", "")
+ algorithm = self._algorithms_dict[digest_algorithm]
+ if (
+ not backend.dsa_parameters_supported(
+ vector['p'], vector['q'], vector['g']
+ ) or not backend.dsa_hash_supported(algorithm)
+ ):
+ pytest.skip(
+ "{0} does not support the provided parameters".format(backend)
+ )
+
+ public_key = dsa.DSAPublicKey(
+ vector['p'], vector['q'], vector['g'], vector['y']
+ )
+ sig = der_encode_dsa_signature(vector['r'], vector['s'])
+ verifier = public_key.verifier(sig, algorithm(), backend)
+ verifier.update(vector['msg'])
+ if vector['result'] == "F":
+ with pytest.raises(InvalidSignature):
+ verifier.verify()
+ else:
+ verifier.verify()
+
+ def test_dsa_verify_invalid_asn1(self, backend):
+ parameters = dsa.DSAParameters.generate(1024, backend)
+ private_key = dsa.DSAPrivateKey.generate(parameters, backend)
+ public_key = private_key.public_key()
+ verifier = public_key.verifier(b'fakesig', hashes.SHA1(), backend)
+ verifier.update(b'fakesig')
+ with pytest.raises(InvalidSignature):
+ verifier.verify()
+
+ def test_use_after_finalize(self, backend):
+ parameters = dsa.DSAParameters.generate(1024, backend)
+ private_key = dsa.DSAPrivateKey.generate(parameters, backend)
+ public_key = private_key.public_key()
+ verifier = public_key.verifier(b'fakesig', hashes.SHA1(), backend)
+ verifier.update(b'irrelevant')
+ with pytest.raises(InvalidSignature):
+ verifier.verify()
+ with pytest.raises(AlreadyFinalized):
+ verifier.verify()
+ with pytest.raises(AlreadyFinalized):
+ verifier.update(b"more data")
+
+ def test_dsa_verifier_invalid_backend(self, backend):
+ pretend_backend = object()
+ params = dsa.DSAParameters.generate(1024, backend)
+ private_key = dsa.DSAPrivateKey.generate(params, backend)
+ public_key = private_key.public_key()
+
+ with raises_unsupported_algorithm(
+ _Reasons.BACKEND_MISSING_INTERFACE):
+ public_key.verifier(b"sig", hashes.SHA1(), pretend_backend)
+
+
+@pytest.mark.dsa
+class TestDSASignature(object):
+ _algorithms_dict = {
+ 'SHA1': hashes.SHA1,
+ 'SHA224': hashes.SHA224,
+ 'SHA256': hashes.SHA256,
+ 'SHA384': hashes.SHA384,
+ 'SHA512': hashes.SHA512}
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "DSA", "FIPS_186-3", "SigGen.txt"),
+ load_fips_dsa_sig_vectors
+ )
+ )
+ def test_dsa_signing(self, vector, backend):
+ digest_algorithm = vector['digest_algorithm'].replace("-", "")
+ algorithm = self._algorithms_dict[digest_algorithm]
+ if (
+ not backend.dsa_parameters_supported(
+ vector['p'], vector['q'], vector['g']
+ ) or not backend.dsa_hash_supported(algorithm)
+ ):
+ pytest.skip(
+ "{0} does not support the provided parameters".format(backend)
+ )
+
+ private_key = dsa.DSAPrivateKey(
+ vector['p'], vector['q'], vector['g'], vector['x'], vector['y']
+ )
+ signer = private_key.signer(algorithm(), backend)
+ signer.update(vector['msg'])
+ signature = signer.finalize()
+ assert signature
+
+ public_key = private_key.public_key()
+ verifier = public_key.verifier(signature, algorithm(), backend)
+ verifier.update(vector['msg'])
+ verifier.verify()
+
+ def test_use_after_finalize(self, backend):
+ parameters = dsa.DSAParameters.generate(1024, backend)
+ private_key = dsa.DSAPrivateKey.generate(parameters, backend)
+ signer = private_key.signer(hashes.SHA1(), backend)
+ signer.update(b"data")
+ signer.finalize()
+ with pytest.raises(AlreadyFinalized):
+ signer.finalize()
+ with pytest.raises(AlreadyFinalized):
+ signer.update(b"more data")
+
+ def test_dsa_signer_invalid_backend(self, backend):
+ pretend_backend = object()
+ params = dsa.DSAParameters.generate(1024, backend)
+ private_key = dsa.DSAPrivateKey.generate(params, backend)
+
+ with raises_unsupported_algorithm(
+ _Reasons.BACKEND_MISSING_INTERFACE):
+ private_key.signer(hashes.SHA1(), pretend_backend)
+
+
def test_dsa_generate_invalid_backend():
pretend_backend = object()
- with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
+ with raises_unsupported_algorithm(
+ _Reasons.BACKEND_MISSING_INTERFACE):
dsa.DSAParameters.generate(1024, pretend_backend)
pretend_parameters = object()
- with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
+ with raises_unsupported_algorithm(
+ _Reasons.BACKEND_MISSING_INTERFACE):
dsa.DSAPrivateKey.generate(pretend_parameters, pretend_backend)
diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py
new file mode 100644
index 00000000..53985fe2
--- /dev/null
+++ b/tests/hazmat/primitives/test_ec.py
@@ -0,0 +1,79 @@
+# 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.
+
+
+from __future__ import absolute_import, division, print_function
+
+import pytest
+
+from cryptography import utils
+from cryptography.hazmat.primitives import interfaces
+from cryptography.hazmat.primitives.asymmetric import ec
+
+
+@utils.register_interface(interfaces.EllipticCurve)
+class DummyCurve(object):
+ name = "dummy-curve"
+
+
+class TestECC(object):
+ def test_ec_numbers(self):
+ numbers = ec.EllipticCurvePrivateNumbers(
+ 1,
+ ec.EllipticCurvePublicNumbers(
+ 2, 3, DummyCurve()
+ )
+ )
+
+ assert numbers.private_value == 1
+ assert numbers.public_numbers.x == 2
+ assert numbers.public_numbers.y == 3
+ assert isinstance(numbers.public_numbers.curve, DummyCurve)
+
+ with pytest.raises(TypeError):
+ ec.EllipticCurvePrivateNumbers(
+ None,
+ ec.EllipticCurvePublicNumbers(
+ 2, 3, DummyCurve()
+ )
+ )
+
+ with pytest.raises(TypeError):
+ ec.EllipticCurvePrivateNumbers(
+ 1,
+ ec.EllipticCurvePublicNumbers(
+ None, 3, DummyCurve()
+ )
+ )
+
+ with pytest.raises(TypeError):
+ ec.EllipticCurvePrivateNumbers(
+ 1,
+ ec.EllipticCurvePublicNumbers(
+ 2, None, DummyCurve()
+ )
+ )
+
+ with pytest.raises(TypeError):
+ ec.EllipticCurvePrivateNumbers(
+ 1,
+ ec.EllipticCurvePublicNumbers(
+ 2, 3, None
+ )
+ )
+
+ with pytest.raises(TypeError):
+ ec.EllipticCurvePrivateNumbers(
+ 1,
+ None
+ )
diff --git a/tests/hazmat/primitives/test_hkdf.py b/tests/hazmat/primitives/test_hkdf.py
index 2e3c0c3d..598f09f0 100644
--- a/tests/hazmat/primitives/test_hkdf.py
+++ b/tests/hazmat/primitives/test_hkdf.py
@@ -13,6 +13,8 @@
from __future__ import absolute_import, division, print_function
+import binascii
+
import pytest
import six
@@ -21,7 +23,7 @@ from cryptography.exceptions import (
AlreadyFinalized, InvalidKey, _Reasons
)
from cryptography.hazmat.primitives import hashes
-from cryptography.hazmat.primitives.kdf.hkdf import HKDF
+from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
from ...utils import raises_unsupported_algorithm
@@ -151,8 +153,67 @@ class TestHKDF(object):
hkdf.verify(b"foo", six.u("bar"))
+@pytest.mark.hmac
+class TestHKDFExpand(object):
+ def test_derive(self, backend):
+ prk = binascii.unhexlify(
+ b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
+ )
+
+ okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c"
+ b"5bf34007208d5b887185865")
+
+ info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+ hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+ assert binascii.hexlify(hkdf.derive(prk)) == okm
+
+ def test_verify(self, backend):
+ prk = binascii.unhexlify(
+ b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
+ )
+
+ okm = (b"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c"
+ b"5bf34007208d5b887185865")
+
+ info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+ hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+ assert hkdf.verify(prk, binascii.unhexlify(okm)) is None
+
+ def test_invalid_verify(self, backend):
+ prk = binascii.unhexlify(
+ b"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"
+ )
+
+ info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+ hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+ with pytest.raises(InvalidKey):
+ hkdf.verify(prk, b"wrong key")
+
+ def test_already_finalized(self, backend):
+ info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+ hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+ hkdf.derive(b"first")
+
+ with pytest.raises(AlreadyFinalized):
+ hkdf.derive(b"second")
+
+ def test_unicode_error(self, backend):
+ info = binascii.unhexlify(b"f0f1f2f3f4f5f6f7f8f9")
+ hkdf = HKDFExpand(hashes.SHA256(), 42, info, backend)
+
+ with pytest.raises(TypeError):
+ hkdf.derive(six.u("first"))
+
+
def test_invalid_backend():
pretend_backend = object()
with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
HKDF(hashes.SHA256(), 16, None, None, pretend_backend)
+
+ with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
+ HKDFExpand(hashes.SHA256(), 16, None, pretend_backend)
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
index 8953db5b..ba668bff 100644
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -28,6 +28,12 @@ from cryptography.exceptions import (
from cryptography.hazmat.primitives import hashes, interfaces
from cryptography.hazmat.primitives.asymmetric import padding, rsa
+from .fixtures_rsa import (
+ RSA_KEY_1024, RSA_KEY_1025, RSA_KEY_1026, RSA_KEY_1027, RSA_KEY_1028,
+ RSA_KEY_1029, RSA_KEY_1030, RSA_KEY_1031, RSA_KEY_1536, RSA_KEY_2048,
+ RSA_KEY_512, RSA_KEY_512_ALT, RSA_KEY_522, RSA_KEY_599, RSA_KEY_745,
+ RSA_KEY_768,
+)
from .utils import (
_check_rsa_private_key, generate_rsa_verification_test
)
@@ -93,7 +99,7 @@ class TestRSA(object):
assert skey.key_size == key_size
assert skey.public_exponent == public_exponent
- def test_generate_bad_rsa_key(self, backend):
+ def test_generate_bad_public_exponent(self, backend):
with pytest.raises(ValueError):
rsa.RSAPrivateKey.generate(public_exponent=1,
key_size=2048,
@@ -376,6 +382,12 @@ def test_rsa_generate_invalid_backend():
@pytest.mark.rsa
class TestRSASignature(object):
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
@pytest.mark.parametrize(
"pkcs1_example",
_flatten_pkcs1_examples(load_vectors_from_file(
@@ -401,6 +413,15 @@ class TestRSASignature(object):
signature = signer.finalize()
assert binascii.hexlify(signature) == example["signature"]
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS."
+ )
@pytest.mark.parametrize(
"pkcs1_example",
_flatten_pkcs1_examples(load_vectors_from_file(
@@ -451,15 +472,22 @@ class TestRSASignature(object):
verifier.update(binascii.unhexlify(example["message"]))
verifier.verify()
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS."
+ )
def test_deprecated_pss_mgf1_salt_length(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
signer = private_key.signer(
- padding.PSS(
- mgf=padding.MGF1(
+ pytest.deprecated_call(
+ padding.PSS,
+ mgf=pytest.deprecated_call(
+ padding.MGF1,
algorithm=hashes.SHA1(),
salt_length=padding.MGF1.MAX_LENGTH
)
@@ -472,8 +500,10 @@ class TestRSASignature(object):
assert len(signature) == math.ceil(private_key.key_size / 8.0)
verifier = private_key.public_key().verifier(
signature,
- padding.PSS(
- mgf=padding.MGF1(
+ pytest.deprecated_call(
+ padding.PSS,
+ mgf=pytest.deprecated_call(
+ padding.MGF1,
algorithm=hashes.SHA1(),
salt_length=padding.MGF1.MAX_LENGTH
)
@@ -489,15 +519,16 @@ class TestRSASignature(object):
[hashes.SHA224(), hashes.SHA256(), hashes.SHA384(), hashes.SHA512()]
)
def test_pss_signing_sha2(self, hash_alg, backend):
- if not backend.mgf1_hash_supported(hash_alg):
+ if not backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hash_alg),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ):
pytest.skip(
- "Does not support {0} with MGF1.".format(hash_alg.name)
+ "Does not support {0} in MGF1 using PSS.".format(hash_alg.name)
)
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=768,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_768)
public_key = private_key.public_key()
pss = padding.PSS(
mgf=padding.MGF1(hash_alg),
@@ -520,15 +551,19 @@ class TestRSASignature(object):
verifier.verify()
@pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA512()),
+ only_if=lambda backend: (
+ backend.hash_supported(hashes.SHA512()) and
+ backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ )
+ ),
skip_message="Does not support SHA512."
)
def test_pss_minimum_key_size_for_digest(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=522,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_522)
signer = private_key.signer(
padding.PSS(
mgf=padding.MGF1(hashes.SHA1()),
@@ -541,15 +576,20 @@ class TestRSASignature(object):
signer.finalize()
@pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS."
+ )
+ @pytest.mark.supported(
only_if=lambda backend: backend.hash_supported(hashes.SHA512()),
skip_message="Does not support SHA512."
)
def test_pss_signing_digest_too_large_for_key_size(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
with pytest.raises(ValueError):
private_key.signer(
padding.PSS(
@@ -560,12 +600,17 @@ class TestRSASignature(object):
backend
)
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS."
+ )
def test_pss_signing_salt_length_too_long(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
signer = private_key.signer(
padding.PSS(
mgf=padding.MGF1(hashes.SHA1()),
@@ -578,12 +623,14 @@ class TestRSASignature(object):
with pytest.raises(ValueError):
signer.finalize()
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
def test_use_after_finalize(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1(), backend)
signer.update(b"sign me")
signer.finalize()
@@ -593,47 +640,43 @@ class TestRSASignature(object):
signer.update(b"more data")
def test_unsupported_padding(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
private_key.signer(DummyPadding(), hashes.SHA1(), backend)
def test_padding_incorrect_type(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
with pytest.raises(TypeError):
private_key.signer("notpadding", hashes.SHA1(), backend)
def test_rsa_signer_invalid_backend(self, backend):
pretend_backend = object()
- private_key = rsa.RSAPrivateKey.generate(65537, 2048, backend)
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_2048)
with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
private_key.signer(
padding.PKCS1v15(), hashes.SHA256, pretend_backend)
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0)
+ ),
+ skip_message="Does not support PSS."
+ )
def test_unsupported_pss_mgf(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF):
private_key.signer(padding.PSS(mgf=DummyMGF()), hashes.SHA1(),
backend)
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
def test_pkcs1_digest_too_large_for_key_size(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=599,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_599)
signer = private_key.signer(
padding.PKCS1v15(),
hashes.SHA512(),
@@ -643,12 +686,14 @@ class TestRSASignature(object):
with pytest.raises(ValueError):
signer.finalize()
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
def test_pkcs1_minimum_key_size(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=745,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_745)
signer = private_key.signer(
padding.PKCS1v15(),
hashes.SHA512(),
@@ -660,6 +705,12 @@ class TestRSASignature(object):
@pytest.mark.rsa
class TestRSAVerification(object):
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
@pytest.mark.parametrize(
"pkcs1_example",
_flatten_pkcs1_examples(load_vectors_from_file(
@@ -683,12 +734,14 @@ class TestRSAVerification(object):
verifier.update(binascii.unhexlify(example["message"]))
verifier.verify()
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
def test_invalid_pkcs1v15_signature_wrong_data(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
public_key = private_key.public_key()
signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1(), backend)
signer.update(b"sign me")
@@ -703,17 +756,15 @@ class TestRSAVerification(object):
with pytest.raises(InvalidSignature):
verifier.verify()
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
def test_invalid_pkcs1v15_signature_wrong_key(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
- private_key2 = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
+ private_key2 = rsa.RSAPrivateKey(**RSA_KEY_512_ALT)
public_key = private_key2.public_key()
signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1(), backend)
signer.update(b"sign me")
@@ -728,6 +779,15 @@ class TestRSAVerification(object):
with pytest.raises(InvalidSignature):
verifier.verify()
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=20
+ )
+ ),
+ skip_message="Does not support PSS."
+ )
@pytest.mark.parametrize(
"pkcs1_example",
_flatten_pkcs1_examples(load_vectors_from_file(
@@ -754,6 +814,15 @@ class TestRSAVerification(object):
verifier.update(binascii.unhexlify(example["message"]))
verifier.verify()
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS."
+ )
def test_invalid_pss_signature_wrong_data(self, backend):
public_key = rsa.RSAPublicKey(
modulus=int(
@@ -780,6 +849,15 @@ class TestRSAVerification(object):
with pytest.raises(InvalidSignature):
verifier.verify()
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS."
+ )
def test_invalid_pss_signature_wrong_key(self, backend):
signature = binascii.unhexlify(
b"3a1880165014ba6eb53cc1449d13e5132ebcc0cfd9ade6d7a2494a0503bd0826"
@@ -808,6 +886,15 @@ class TestRSAVerification(object):
with pytest.raises(InvalidSignature):
verifier.verify()
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS."
+ )
def test_invalid_pss_signature_data_too_large_for_modulus(self, backend):
signature = binascii.unhexlify(
b"cb43bde4f7ab89eb4a79c6e8dd67e0d1af60715da64429d90c716a490b799c29"
@@ -836,12 +923,14 @@ class TestRSAVerification(object):
with pytest.raises(InvalidSignature):
verifier.verify()
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
def test_use_after_finalize(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
public_key = private_key.public_key()
signer = private_key.signer(padding.PKCS1v15(), hashes.SHA1(), backend)
signer.update(b"sign me")
@@ -861,21 +950,13 @@ class TestRSAVerification(object):
verifier.update(b"more data")
def test_unsupported_padding(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
public_key = private_key.public_key()
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
public_key.verifier(b"sig", DummyPadding(), hashes.SHA1(), backend)
def test_padding_incorrect_type(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
public_key = private_key.public_key()
with pytest.raises(TypeError):
public_key.verifier(b"sig", "notpadding", hashes.SHA1(), backend)
@@ -889,27 +970,34 @@ class TestRSAVerification(object):
public_key.verifier(
b"foo", padding.PKCS1v15(), hashes.SHA256(), pretend_backend)
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(mgf=padding.MGF1(hashes.SHA1()), salt_length=0)
+ ),
+ skip_message="Does not support PSS."
+ )
def test_unsupported_pss_mgf(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
public_key = private_key.public_key()
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF):
public_key.verifier(b"sig", padding.PSS(mgf=DummyMGF()),
hashes.SHA1(), backend)
@pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS."
+ )
+ @pytest.mark.supported(
only_if=lambda backend: backend.hash_supported(hashes.SHA512()),
skip_message="Does not support SHA512."
)
def test_pss_verify_digest_too_large_for_key_size(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
signature = binascii.unhexlify(
b"8b9a3ae9fb3b64158f3476dd8d8a1f1425444e98940e0926378baa9944d219d8"
b"534c050ef6b19b1bdc6eb4da422e89161106a6f5b5cc16135b11eb6439b646bd"
@@ -926,6 +1014,15 @@ class TestRSAVerification(object):
backend
)
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS."
+ )
def test_pss_verify_salt_length_too_long(self, backend):
signature = binascii.unhexlify(
b"8b9a3ae9fb3b64158f3476dd8d8a1f1425444e98940e0926378baa9944d219d8"
@@ -944,8 +1041,8 @@ class TestRSAVerification(object):
padding.PSS(
mgf=padding.MGF1(
algorithm=hashes.SHA1(),
- salt_length=1000000
- )
+ ),
+ salt_length=1000000
),
hashes.SHA1(),
backend
@@ -958,8 +1055,13 @@ class TestRSAVerification(object):
@pytest.mark.rsa
class TestRSAPSSMGF1Verification(object):
test_rsa_pss_mgf1_sha1 = pytest.mark.supported(
- only_if=lambda backend: backend.mgf1_hash_supported(hashes.SHA1()),
- skip_message="Does not support SHA1 with MGF1."
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA1()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS using MGF1 with SHA1."
)(generate_rsa_verification_test(
load_rsa_nist_vectors,
os.path.join("asymmetric", "RSA", "FIPS_186-2"),
@@ -972,14 +1074,19 @@ class TestRSAPSSMGF1Verification(object):
lambda params, hash_alg: padding.PSS(
mgf=padding.MGF1(
algorithm=hash_alg,
- salt_length=params["salt_length"]
- )
+ ),
+ salt_length=params["salt_length"]
)
))
test_rsa_pss_mgf1_sha224 = pytest.mark.supported(
- only_if=lambda backend: backend.mgf1_hash_supported(hashes.SHA224()),
- skip_message="Does not support SHA224 with MGF1."
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA224()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS using MGF1 with SHA224."
)(generate_rsa_verification_test(
load_rsa_nist_vectors,
os.path.join("asymmetric", "RSA", "FIPS_186-2"),
@@ -992,14 +1099,19 @@ class TestRSAPSSMGF1Verification(object):
lambda params, hash_alg: padding.PSS(
mgf=padding.MGF1(
algorithm=hash_alg,
- salt_length=params["salt_length"]
- )
+ ),
+ salt_length=params["salt_length"]
)
))
test_rsa_pss_mgf1_sha256 = pytest.mark.supported(
- only_if=lambda backend: backend.mgf1_hash_supported(hashes.SHA256()),
- skip_message="Does not support SHA256 with MGF1."
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA256()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS using MGF1 with SHA256."
)(generate_rsa_verification_test(
load_rsa_nist_vectors,
os.path.join("asymmetric", "RSA", "FIPS_186-2"),
@@ -1012,14 +1124,19 @@ class TestRSAPSSMGF1Verification(object):
lambda params, hash_alg: padding.PSS(
mgf=padding.MGF1(
algorithm=hash_alg,
- salt_length=params["salt_length"]
- )
+ ),
+ salt_length=params["salt_length"]
)
))
test_rsa_pss_mgf1_sha384 = pytest.mark.supported(
- only_if=lambda backend: backend.mgf1_hash_supported(hashes.SHA384()),
- skip_message="Does not support SHA384 with MGF1."
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA384()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS using MGF1 with SHA384."
)(generate_rsa_verification_test(
load_rsa_nist_vectors,
os.path.join("asymmetric", "RSA", "FIPS_186-2"),
@@ -1032,14 +1149,19 @@ class TestRSAPSSMGF1Verification(object):
lambda params, hash_alg: padding.PSS(
mgf=padding.MGF1(
algorithm=hash_alg,
- salt_length=params["salt_length"]
- )
+ ),
+ salt_length=params["salt_length"]
)
))
test_rsa_pss_mgf1_sha512 = pytest.mark.supported(
- only_if=lambda backend: backend.mgf1_hash_supported(hashes.SHA512()),
- skip_message="Does not support SHA512 with MGF1."
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA512()),
+ salt_length=padding.PSS.MAX_LENGTH
+ )
+ ),
+ skip_message="Does not support PSS using MGF1 with SHA512."
)(generate_rsa_verification_test(
load_rsa_nist_vectors,
os.path.join("asymmetric", "RSA", "FIPS_186-2"),
@@ -1052,8 +1174,8 @@ class TestRSAPSSMGF1Verification(object):
lambda params, hash_alg: padding.PSS(
mgf=padding.MGF1(
algorithm=hash_alg,
- salt_length=params["salt_length"]
- )
+ ),
+ salt_length=params["salt_length"]
)
))
@@ -1061,8 +1183,11 @@ class TestRSAPSSMGF1Verification(object):
@pytest.mark.rsa
class TestRSAPKCS1Verification(object):
test_rsa_pkcs1v15_verify_sha1 = pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA1()),
- skip_message="Does not support SHA1."
+ only_if=lambda backend: (
+ backend.hash_supported(hashes.SHA1()) and
+ backend.rsa_padding_supported(padding.PKCS1v15())
+ ),
+ skip_message="Does not support SHA1 and PKCS1v1.5."
)(generate_rsa_verification_test(
load_rsa_nist_vectors,
os.path.join("asymmetric", "RSA", "FIPS_186-2"),
@@ -1076,8 +1201,11 @@ class TestRSAPKCS1Verification(object):
))
test_rsa_pkcs1v15_verify_sha224 = pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA224()),
- skip_message="Does not support SHA224."
+ only_if=lambda backend: (
+ backend.hash_supported(hashes.SHA224()) and
+ backend.rsa_padding_supported(padding.PKCS1v15())
+ ),
+ skip_message="Does not support SHA224 and PKCS1v1.5."
)(generate_rsa_verification_test(
load_rsa_nist_vectors,
os.path.join("asymmetric", "RSA", "FIPS_186-2"),
@@ -1091,8 +1219,11 @@ class TestRSAPKCS1Verification(object):
))
test_rsa_pkcs1v15_verify_sha256 = pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA256()),
- skip_message="Does not support SHA256."
+ only_if=lambda backend: (
+ backend.hash_supported(hashes.SHA256()) and
+ backend.rsa_padding_supported(padding.PKCS1v15())
+ ),
+ skip_message="Does not support SHA256 and PKCS1v1.5."
)(generate_rsa_verification_test(
load_rsa_nist_vectors,
os.path.join("asymmetric", "RSA", "FIPS_186-2"),
@@ -1106,8 +1237,11 @@ class TestRSAPKCS1Verification(object):
))
test_rsa_pkcs1v15_verify_sha384 = pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA384()),
- skip_message="Does not support SHA384."
+ only_if=lambda backend: (
+ backend.hash_supported(hashes.SHA384()) and
+ backend.rsa_padding_supported(padding.PKCS1v15())
+ ),
+ skip_message="Does not support SHA384 and PKCS1v1.5."
)(generate_rsa_verification_test(
load_rsa_nist_vectors,
os.path.join("asymmetric", "RSA", "FIPS_186-2"),
@@ -1121,8 +1255,11 @@ class TestRSAPKCS1Verification(object):
))
test_rsa_pkcs1v15_verify_sha512 = pytest.mark.supported(
- only_if=lambda backend: backend.hash_supported(hashes.SHA512()),
- skip_message="Does not support SHA512."
+ only_if=lambda backend: (
+ backend.hash_supported(hashes.SHA512()) and
+ backend.rsa_padding_supported(padding.PKCS1v15())
+ ),
+ skip_message="Does not support SHA512 and PKCS1v1.5."
)(generate_rsa_verification_test(
load_rsa_nist_vectors,
os.path.join("asymmetric", "RSA", "FIPS_186-2"),
@@ -1226,6 +1363,12 @@ class TestOAEP(object):
@pytest.mark.rsa
class TestRSADecryption(object):
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
@pytest.mark.parametrize(
"vector",
_flatten_pkcs1_examples(load_vectors_from_file(
@@ -1256,20 +1399,18 @@ class TestRSADecryption(object):
assert message == binascii.unhexlify(example["message"])
def test_unsupported_padding(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
private_key.decrypt(b"0" * 64, DummyPadding(), backend)
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
def test_decrypt_invalid_decrypt(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
with pytest.raises(ValueError):
private_key.decrypt(
b"\x00" * 64,
@@ -1277,12 +1418,14 @@ class TestRSADecryption(object):
backend
)
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
def test_decrypt_ciphertext_too_large(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
with pytest.raises(ValueError):
private_key.decrypt(
b"\x00" * 65,
@@ -1290,12 +1433,14 @@ class TestRSADecryption(object):
backend
)
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
def test_decrypt_ciphertext_too_small(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
ct = binascii.unhexlify(
b"50b4c14136bd198c2f3c3ed243fce036e168d56517984a263cd66492b80804f1"
b"69d210f2b9bdfb48b12f9ea05009c77da257cc600ccefe3a6283789d8ea0"
@@ -1318,6 +1463,16 @@ class TestRSADecryption(object):
pretend_backend
)
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ algorithm=hashes.SHA1(),
+ label=None
+ )
+ ),
+ skip_message="Does not support OAEP."
+ )
@pytest.mark.parametrize(
"vector",
_flatten_pkcs1_examples(load_vectors_from_file(
@@ -1350,11 +1505,7 @@ class TestRSADecryption(object):
assert message == binascii.unhexlify(example["message"])
def test_unsupported_oaep_mgf(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF):
private_key.decrypt(
b"0" * 64,
@@ -1369,26 +1520,66 @@ class TestRSADecryption(object):
@pytest.mark.rsa
class TestRSAEncryption(object):
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ algorithm=hashes.SHA1(),
+ label=None
+ )
+ ),
+ skip_message="Does not support OAEP."
+ )
@pytest.mark.parametrize(
- ("key_size", "pad"),
+ ("key_data", "pad"),
itertools.product(
- (1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1536, 2048),
- (
+ (RSA_KEY_1024, RSA_KEY_1025, RSA_KEY_1026, RSA_KEY_1027,
+ RSA_KEY_1028, RSA_KEY_1029, RSA_KEY_1030, RSA_KEY_1031,
+ RSA_KEY_1536, RSA_KEY_2048),
+ [
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=None
- ),
- padding.PKCS1v15()
- )
+ )
+ ]
)
)
- def test_rsa_encrypt(self, key_size, pad, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=key_size,
- backend=backend
+ def test_rsa_encrypt_oaep(self, key_data, pad, backend):
+ private_key = rsa.RSAPrivateKey(**key_data)
+ pt = b"encrypt me!"
+ public_key = private_key.public_key()
+ ct = public_key.encrypt(
+ pt,
+ pad,
+ backend
)
+ assert ct != pt
+ assert len(ct) == math.ceil(public_key.key_size / 8.0)
+ recovered_pt = private_key.decrypt(
+ ct,
+ pad,
+ backend
+ )
+ assert recovered_pt == pt
+
+ @pytest.mark.supported(
+ only_if=lambda backend: backend.rsa_padding_supported(
+ padding.PKCS1v15()
+ ),
+ skip_message="Does not support PKCS1v1.5."
+ )
+ @pytest.mark.parametrize(
+ ("key_data", "pad"),
+ itertools.product(
+ (RSA_KEY_1024, RSA_KEY_1025, RSA_KEY_1026, RSA_KEY_1027,
+ RSA_KEY_1028, RSA_KEY_1029, RSA_KEY_1030, RSA_KEY_1031,
+ RSA_KEY_1536, RSA_KEY_2048),
+ [padding.PKCS1v15()]
+ )
+ )
+ def test_rsa_encrypt_pkcs1v15(self, key_data, pad, backend):
+ private_key = rsa.RSAPrivateKey(**key_data)
pt = b"encrypt me!"
public_key = private_key.public_key()
ct = public_key.encrypt(
@@ -1406,9 +1597,11 @@ class TestRSAEncryption(object):
assert recovered_pt == pt
@pytest.mark.parametrize(
- ("key_size", "pad"),
+ ("key_data", "pad"),
itertools.product(
- (1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1536, 2048),
+ (RSA_KEY_1024, RSA_KEY_1025, RSA_KEY_1026, RSA_KEY_1027,
+ RSA_KEY_1028, RSA_KEY_1029, RSA_KEY_1030, RSA_KEY_1031,
+ RSA_KEY_1536, RSA_KEY_2048),
(
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
@@ -1419,17 +1612,13 @@ class TestRSAEncryption(object):
)
)
)
- def test_rsa_encrypt_key_too_small(self, key_size, pad, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=key_size,
- backend=backend
- )
+ def test_rsa_encrypt_key_too_small(self, key_data, pad, backend):
+ private_key = rsa.RSAPrivateKey(**key_data)
public_key = private_key.public_key()
# Slightly smaller than the key size but not enough for padding.
with pytest.raises(ValueError):
public_key.encrypt(
- b"\x00" * (key_size // 8 - 1),
+ b"\x00" * (private_key.key_size // 8 - 1),
pad,
backend
)
@@ -1437,7 +1626,7 @@ class TestRSAEncryption(object):
# Larger than the key size.
with pytest.raises(ValueError):
public_key.encrypt(
- b"\x00" * (key_size // 8 + 5),
+ b"\x00" * (private_key.key_size // 8 + 5),
pad,
backend
)
@@ -1455,22 +1644,14 @@ class TestRSAEncryption(object):
)
def test_unsupported_padding(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
public_key = private_key.public_key()
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
public_key.encrypt(b"somedata", DummyPadding(), backend)
def test_unsupported_oaep_mgf(self, backend):
- private_key = rsa.RSAPrivateKey.generate(
- public_exponent=65537,
- key_size=512,
- backend=backend
- )
+ private_key = rsa.RSAPrivateKey(**RSA_KEY_512)
public_key = private_key.public_key()
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF):
diff --git a/tests/hazmat/primitives/test_serialization.py b/tests/hazmat/primitives/test_serialization.py
new file mode 100644
index 00000000..b19990e0
--- /dev/null
+++ b/tests/hazmat/primitives/test_serialization.py
@@ -0,0 +1,546 @@
+# 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.
+
+
+from __future__ import absolute_import, division, print_function
+
+import os
+import textwrap
+
+import pytest
+
+from cryptography.exceptions import _Reasons
+from cryptography.hazmat.primitives.asymmetric import dsa, rsa
+from cryptography.hazmat.primitives.serialization import (
+ load_pem_pkcs8_private_key,
+ load_pem_traditional_openssl_private_key
+)
+
+from .utils import _check_rsa_private_key, load_vectors_from_file
+from ...utils import raises_unsupported_algorithm
+
+
+@pytest.mark.traditional_openssl_serialization
+class TestTraditionalOpenSSLSerialisation(object):
+ @pytest.mark.parametrize(
+ ("key_file", "password"),
+ [
+ ("key1.pem", b"123456"),
+ ("key2.pem", b"a123456"),
+ ("testrsa.pem", None),
+ ("testrsa-encrypted.pem", b"password"),
+ ]
+ )
+ def test_load_pem_rsa_private_key(self, key_file, password, backend):
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "Traditional_OpenSSL_Serialization", key_file),
+ lambda pemfile: load_pem_traditional_openssl_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+
+ assert key
+ assert isinstance(key, rsa.RSAPrivateKey)
+ _check_rsa_private_key(key)
+
+ @pytest.mark.parametrize(
+ ("key_file", "password"),
+ [
+ ("dsa.1024.pem", None),
+ ("dsa.2048.pem", None),
+ ("dsa.3072.pem", None),
+ ]
+ )
+ def test_load_pem_dsa_private_key(self, key_file, password, backend):
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "Traditional_OpenSSL_Serialization", key_file),
+ lambda pemfile: load_pem_traditional_openssl_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+
+ assert key
+ assert isinstance(key, dsa.DSAPrivateKey)
+
+ def test_key1_pem_encrypted_values(self, backend):
+ pkey = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "Traditional_OpenSSL_Serialization", "key1.pem"),
+ lambda pemfile: load_pem_traditional_openssl_private_key(
+ pemfile.read().encode(), b"123456", backend
+ )
+ )
+ assert pkey
+
+ assert pkey.p == int(
+ "fb7d316fc51531b36d93adaefaf52db6ad5beb793d37c4cf9dfc1ddd17cfbafb",
+ 16
+ )
+ assert pkey.q == int(
+ "df98264e646de9a0fbeab094e31caad5bc7adceaaae3c800ca0275dd4bb307f5",
+ 16
+ )
+ assert pkey.private_exponent == int(
+ "db4848c36f478dd5d38f35ae519643b6b810d404bcb76c00e44015e56ca1cab0"
+ "7bb7ae91f6b4b43fcfc82a47d7ed55b8c575152116994c2ce5325ec24313b911",
+ 16
+ )
+ assert pkey.dmp1 == int(
+ "ce997f967192c2bcc3853186f1559fd355c190c58ddc15cbf5de9b6df954c727",
+ 16
+ )
+ assert pkey.dmq1 == int(
+ "b018a57ab20ffaa3862435445d863369b852cf70a67c55058213e3fe10e3848d",
+ 16
+ )
+ assert pkey.iqmp == int(
+ "6a8d830616924f5cf2d1bc1973f97fde6b63e052222ac7be06aa2532d10bac76",
+ 16
+ )
+ assert pkey.public_exponent == 65537
+ assert pkey.modulus == int(
+ "dba786074f2f0350ce1d99f5aed5b520cfe0deb5429ec8f2a88563763f566e77"
+ "9814b7c310e5326edae31198eed439b845dd2db99eaa60f5c16a43f4be6bcf37",
+ 16
+ )
+
+ def test_unused_password(self, backend):
+ key_file = os.path.join(
+ "asymmetric", "Traditional_OpenSSL_Serialization", "testrsa.pem")
+ password = b"this password will not be used"
+
+ with pytest.raises(TypeError):
+ load_vectors_from_file(
+ key_file,
+ lambda pemfile: load_pem_traditional_openssl_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+
+ def test_wrong_password(self, backend):
+ key_file = os.path.join(
+ "asymmetric",
+ "Traditional_OpenSSL_Serialization",
+ "testrsa-encrypted.pem"
+ )
+ password = b"this password is wrong"
+
+ with pytest.raises(ValueError):
+ load_vectors_from_file(
+ key_file,
+ lambda pemfile: load_pem_traditional_openssl_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+
+ @pytest.mark.parametrize("password", [None, b""])
+ def test_missing_password(self, backend, password):
+ key_file = os.path.join(
+ "asymmetric",
+ "Traditional_OpenSSL_Serialization",
+ "testrsa-encrypted.pem"
+ )
+
+ with pytest.raises(TypeError):
+ load_vectors_from_file(
+ key_file,
+ lambda pemfile: load_pem_traditional_openssl_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+
+ def test_wrong_format(self, backend):
+ key_data = b"---- NOT A KEY ----\n"
+
+ with pytest.raises(ValueError):
+ load_pem_traditional_openssl_private_key(
+ key_data, None, backend
+ )
+
+ with pytest.raises(ValueError):
+ load_pem_traditional_openssl_private_key(
+ key_data, b"this password will not be used", backend
+ )
+
+ def test_corrupt_format(self, backend):
+ # privkey.pem with a bunch of data missing.
+ key_data = textwrap.dedent("""\
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIBPAIBAAJBAKrbeqkuRk8VcRmWFmtP+LviMB3+6dizWW3DwaffznyHGAFwUJ/I
+ Tv0XtbsCyl3QoyKGhrOAy3RvPK5M38iuXT0CAwEAAQJAZ3cnzaHXM/bxGaR5CR1R
+ rD1qFBAVfoQFiOH9uPJgMaoAuoQEisPHVcZDKcOv4wEg6/TInAIXBnEigtqvRzuy
+ mvcpHZwQJdmdHHkGKAs37Dfxi67HbkUCIQCeZGliHXFa071Fp06ZeWlR2ADonTZz
+ rJBhdTe0v5pCeQIhAIZfkiGgGBX4cIuuckzEm43g9WMUjxP/0GlK39vIyihxAiEA
+ mymehFRT0MvqW5xAKAx7Pgkt8HVKwVhc2LwGKHE0DZM=
+ -----END RSA PRIVATE KEY-----
+ """).encode()
+
+ with pytest.raises(ValueError):
+ load_pem_traditional_openssl_private_key(
+ key_data, None, backend
+ )
+
+ with pytest.raises(ValueError):
+ load_pem_traditional_openssl_private_key(
+ key_data, b"this password will not be used", backend
+ )
+
+ def test_encrypted_corrupt_format(self, backend):
+ # privkey.pem with a single bit flipped
+ key_data = textwrap.dedent("""\
+ -----BEGIN RSA PRIVATE KEY-----
+ Proc-Type: <,ENCRYPTED
+ DEK-Info: AES-128-CBC,5E22A2BD85A653FB7A3ED20DE84F54CD
+
+ hAqtb5ZkTMGcs4BBDQ1SKZzdQThWRDzEDxM3qBfjvYa35KxZ54aic013mW/lwj2I
+ v5bbpOjrHYHNAiZYZ7RNb+ztbF6F/g5PA5g7mFwEq+LFBY0InIplYBSv9QtE+lot
+ Dy4AlZa/+NzJwgdKDb+JVfk5SddyD4ywnyeORnMPy4xXKvjXwmW+iLibZVKsjIgw
+ H8hSxcD+FhWyJm9h9uLtmpuqhQo0jTUYpnTezZx2xeVPB53Ev7YCxR9Nsgj5GsVf
+ 9Z/hqLB7IFgM3pa0z3PQeUIZF/cEf72fISWIOBwwkzVrPUkXWfbuWeJXQXSs3amE
+ 5A295jD9BQp9CY0nNFSsy+qiXWToq2xT3y5zVNEStmN0SCGNaIlUnJzL9IHW+oMI
+ kPmXZMnAYBWeeCF1gf3J3aE5lZInegHNfEI0+J0LazC2aNU5Dg/BNqrmRqKWEIo/
+ -----END RSA PRIVATE KEY-----
+ """).encode()
+
+ password = b"this password is wrong"
+
+ with pytest.raises(ValueError):
+ load_pem_traditional_openssl_private_key(
+ key_data, None, backend
+ )
+
+ with pytest.raises(ValueError):
+ load_pem_traditional_openssl_private_key(
+ key_data, password, backend
+ )
+
+ def test_unsupported_key_encryption(self, backend):
+ key_data = textwrap.dedent("""\
+ -----BEGIN RSA PRIVATE KEY-----
+ Proc-Type: 4,ENCRYPTED
+ DEK-Info: FAKE-123,5E22A2BD85A653FB7A3ED20DE84F54CD
+
+ hAqtb5ZkTMGcs4BBDQ1SKZzdQThWRDzEDxM3qBfjvYa35KxZ54aic013mW/lwj2I
+ v5bbpOjrHYHNAiZYZ7RNb+ztbF6F/g5PA5g7mFwEq+LFBY0InIplYBSv9QtE+lot
+ Dy4AlZa/+NzJwgdKDb+JVfk5SddyD4ywnyeORnMPy4xXKvjXwmW+iLibZVKsjIgw
+ H8hSxcD+FhWyJm9h9uLtmpuqhQo0jTUYpnTezZx2xeVPB53Ev7YCxR9Nsgj5GsVf
+ 9Z/hqLB7IFgM3pa0z3PQeUIZF/cEf72fISWIOBwwkzVrPUkXWfbuWeJXQXSs3amE
+ 5A295jD9BQp9CY0nNFSsy+qiXWToq2xT3y5zVNEStmN0SCGNaIlUnJzL9IHW+oMI
+ kPmXZMnAYBWeeCF1gf3J3aE5lZInegHNfEI0+J0LazC2aNU5Dg/BNqrmRqKWEIo/
+ -----END RSA PRIVATE KEY-----
+ """).encode()
+
+ password = b"password"
+
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
+ load_pem_traditional_openssl_private_key(
+ key_data, password, backend
+ )
+
+
+@pytest.mark.pkcs8_serialization
+class TestPKCS8Serialisation(object):
+ @pytest.mark.parametrize(
+ ("key_file", "password"),
+ [
+ ("unencpkcs8.pem", None),
+ ("encpkcs8.pem", b"foobar"),
+ ("enc2pkcs8.pem", b"baz"),
+ ("pkcs12_s2k_pem-X_9607.pem", b"123456"),
+ ("pkcs12_s2k_pem-X_9671.pem", b"123456"),
+ ("pkcs12_s2k_pem-X_9925.pem", b"123456"),
+ ("pkcs12_s2k_pem-X_9926.pem", b"123456"),
+ ("pkcs12_s2k_pem-X_9927.pem", b"123456"),
+ ("pkcs12_s2k_pem-X_9928.pem", b"123456"),
+ ("pkcs12_s2k_pem-X_9929.pem", b"123456"),
+ ("pkcs12_s2k_pem-X_9930.pem", b"123456"),
+ ("pkcs12_s2k_pem-X_9931.pem", b"123456"),
+ ("pkcs12_s2k_pem-X_9932.pem", b"123456"),
+ ]
+ )
+ def test_load_pem_rsa_private_key(self, key_file, password, backend):
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", key_file),
+ lambda pemfile: load_pem_pkcs8_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+
+ assert key
+ assert isinstance(key, rsa.RSAPrivateKey)
+ _check_rsa_private_key(key)
+
+ def test_unused_password(self, backend):
+ key_file = os.path.join(
+ "asymmetric", "PKCS8", "unencpkcs8.pem")
+ password = b"this password will not be used"
+
+ with pytest.raises(TypeError):
+ load_vectors_from_file(
+ key_file,
+ lambda pemfile: load_pem_pkcs8_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+
+ def test_wrong_password(self, backend):
+ key_file = os.path.join(
+ "asymmetric", "PKCS8", "encpkcs8.pem")
+ password = b"this password is wrong"
+
+ with pytest.raises(ValueError):
+ load_vectors_from_file(
+ key_file,
+ lambda pemfile: load_pem_pkcs8_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+
+ @pytest.mark.parametrize("password", [None, b""])
+ def test_missing_password(self, backend, password):
+ key_file = os.path.join(
+ "asymmetric",
+ "PKCS8",
+ "encpkcs8.pem"
+ )
+
+ with pytest.raises(TypeError):
+ load_vectors_from_file(
+ key_file,
+ lambda pemfile: load_pem_pkcs8_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+
+ def test_wrong_format(self, backend):
+ key_data = b"---- NOT A KEY ----\n"
+
+ with pytest.raises(ValueError):
+ load_pem_pkcs8_private_key(
+ key_data, None, backend
+ )
+
+ with pytest.raises(ValueError):
+ load_pem_pkcs8_private_key(
+ key_data, b"this password will not be used", backend
+ )
+
+ def test_corrupt_format(self, backend):
+ # unencpkcs8.pem with a bunch of data missing.
+ key_data = textwrap.dedent("""\
+ -----BEGIN PRIVATE KEY-----
+ MIICdQIBADALBgkqhkiG9w0BAQEEggJhMIICXQIBAAKBgQC7JHoJfg6yNzLMOWet
+ 8Z49a4KD0dCspMAYvo2YAMB7/wdEycocujbhJ2n/seONi+5XqTqqFkM5VBl8rmkk
+ FPZk/7x0xmdsTPECSWnHK+HhoaNDFPR3j8jQhVo1laxiqcEhAHegi5cwtFosuJAv
+ FiRC0Cgz+frQPFQEBsAV9RuasyQxqzxrR0Ow0qncBeGBWbYE6WZhqtcLAI895b+i
+ +F4lbB4iD7T9QeIDMU/aIMXA81UO4cns1z4qDAHKeyLLrPQrJ/B4X7XC+egUWm5+
+ hr1qmyAMusyXIBECQQDJWZ8piluf4yrYfsJAn6hF5T4RjTztbqvO0GVG2McHY7Uj
+ NPSffhzHx/ll0fQEQji+OgydCCX8o3HZrgw5YfSJAkEA7e+rqdU5nO5ZG//PSEQb
+ tjLnRiTzBH/elQhtdZ5nF7pcpNTi4k13zutmKcWW4GK75azcRGJUhu1kDM7QYAOd
+ SQJAVNkYcifkvna7GmooL5VYEsQsqLbM4v0NF2TIGNfG3z1MGp75KrC5LhL97MNR
+ we2p/bd2k0HYyCKUGnf2nMPDiQJBAI75pwittSoE240EobUGIDTSz8CJsXIxuDmL
+ z+KOpdpPRR5TQmbEMEspjsFpFymMiuYPgmihQbO2cJl1qScY5OkCQQCJ6m5tcN8l
+ Xxg/SNpjEIv+qAyUD96XVlOJlOIeLHQ8kYE0C6ZA+MsqYIzgAreJk88Yn0lU/X0/
+ mu/UpE/BRZmR
+ -----END PRIVATE KEY-----
+ """).encode()
+
+ with pytest.raises(ValueError):
+ load_pem_pkcs8_private_key(
+ key_data, None, backend
+ )
+
+ with pytest.raises(ValueError):
+ load_pem_pkcs8_private_key(
+ key_data, b"this password will not be used", backend
+ )
+
+ def test_encrypted_corrupt_format(self, backend):
+ # encpkcs8.pem with some bits flipped.
+ key_data = textwrap.dedent("""\
+ -----BEGIN ENCRYPTED PRIVATE KEY-----
+ MIICojAcBgoqhkiG9w0BDAEDMA4ECHK0M0+QuEL9AgIBIcSCAoDRq+KRY+0XP0tO
+ lwBTzViiXSXoyNnKAZKt5r5K/fGNntv22g/1s/ZNCetrqsJDC5eMUPPacz06jFq/
+ Ipsep4/OgjQ9UAOzXNrWEoNyrHnWDo7usgD3CW0mKyqER4+wG0adVMbt3N+CJHGB
+ 85jzRmQTfkdx1rSWeSx+XyswHn8ER4+hQ+omKWMVm7AFkjjmP/KnhUnLT98J8rhU
+ ArQoFPHz/6HVkypFccNaPPNg6IA4aS2A+TU9vJYOaXSVfFB2yf99hfYYzC+ukmuU
+ 5Lun0cysK5s/5uSwDueUmDQKspnaNyiaMGDxvw8hilJc7vg0fGObfnbIpizhxJwq
+ gKBfR7Zt0Hv8OYi1He4MehfMGdbHskztF+yQ40LplBGXQrvAqpU4zShga1BoQ98T
+ 0ekbBmqj7hg47VFsppXR7DKhx7G7rpMmdKbFhAZVCjae7rRGpUtD52cpFdPhMyAX
+ huhMkoczwUW8B/rM4272lkHo6Br0yk/TQfTEGkvryflNVu6lniPTV151WV5U1M3o
+ 3G3a44eDyt7Ln+WSOpWtbPQMTrpKhur6WXgJvrpa/m02oOGdvOlDsoOCgavgQMWg
+ 7xKKL7620pHl7p7f/8tlE8q6vLXVvyNtAOgt/JAr2rgvrHaZSzDE0DwgCjBXEm+7
+ cVMVNkHod7bLQefVanVtWqPzbmr8f7gKeuGwWSG9oew/lN2hxcLEPJHAQlnLgx3P
+ 0GdGjK9NvwA0EP2gYIeE4+UtSder7xQ7bVh25VB20R4TTIIs4aXXCVOoQPagnzaT
+ 6JLgl8FrvdfjHwIvmSOO1YMNmILBq000Q8WDqyErBDs4hsvtO6VQ4LeqJj6gClX3
+ qeJNaJFu
+ -----END ENCRYPTED PRIVATE KEY-----
+ """).encode()
+
+ password = b"this password is wrong"
+
+ with pytest.raises(ValueError):
+ load_pem_pkcs8_private_key(
+ key_data, None, backend
+ )
+
+ with pytest.raises(ValueError):
+ load_pem_pkcs8_private_key(
+ key_data, password, backend
+ )
+
+ def test_key1_pem_encrypted_values(self, backend):
+ pkey = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", "encpkcs8.pem"),
+ lambda pemfile: load_pem_pkcs8_private_key(
+ pemfile.read().encode(), b"foobar", backend
+ )
+ )
+ assert pkey
+
+ assert pkey.modulus == int(
+ "00beec64d6db5760ac2fd4c971145641b9bd7f5c56558ece608795c79807"
+ "376a7fe5b19f95b35ca358ea5c8abd7ae051d49cd2f1e45969a1ae945460"
+ "3c14b278664a0e414ebc8913acb6203626985525e17a600611b028542dd0"
+ "562aad787fb4f1650aa318cdcff751e1b187cbf6785fbe164e9809491b95"
+ "dd68480567c99b1a57", 16
+ )
+
+ assert pkey.public_exponent == 65537
+
+ assert pkey.private_exponent == int(
+ "0cfe316e9dc6b8817f4fcfd5ae38a0886f68f773b8a6db4c9e6d8703c599"
+ "f3d9785c3a2c09e4c8090909fb3721e19a3009ec21221523a729265707a5"
+ "8f13063671c42a4096cad378ef2510cb59e23071489d8893ac4934dd149f"
+ "34f2d094bea57f1c8027c3a77248ac9b91218737d0c3c3dfa7d7829e6977"
+ "cf7d995688c86c81", 16
+ )
+
+ assert pkey.p == int(
+ "00db122ac857b2c0437d7616daa98e597bb75ca9ad3a47a70bec10c10036"
+ "03328794b225c8e3eee6ffd3fd6d2253d28e071fe27d629ab072faa14377"
+ "ce6118cb67", 16
+ )
+
+ assert pkey.q == int(
+ "00df1b8aa8506fcbbbb9d00257f2975e38b33d2698fd0f37e82d7ef38c56"
+ "f21b6ced63c825383782a7115cfcc093300987dbd2853b518d1c8f26382a"
+ "2d2586d391", 16
+ )
+
+ assert pkey.dmp1 == int(
+ "00be18aca13e60712fdf5daa85421eb10d86d654b269e1255656194fb0c4"
+ "2dd01a1070ea12c19f5c39e09587af02f7b1a1030d016a9ffabf3b36d699"
+ "ceaf38d9bf", 16
+ )
+
+ assert pkey.dmq1 == int(
+ "71aa8978f90a0c050744b77cf1263725b203ac9f730606d8ae1d289dce4a"
+ "28b8d534e9ea347aeb808c73107e583eb80c546d2bddadcdb3c82693a4c1"
+ "3d863451", 16
+ )
+
+ assert pkey.iqmp == int(
+ "136b7b1afac6e6279f71b24217b7083485a5e827d156024609dae39d48a6"
+ "bdb55af2f062cc4a3b077434e6fffad5faa29a2b5dba2bed3e4621e478c0"
+ "97ccfe7f", 16
+ )
+
+ @pytest.mark.parametrize(
+ ("key_file", "password"),
+ [
+ ("unenc-dsa-pkcs8.pem", None),
+ ]
+ )
+ def test_load_pem_dsa_private_key(self, key_file, password, backend):
+ key = load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", key_file),
+ lambda pemfile: load_pem_traditional_openssl_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+ assert key
+ assert isinstance(key, dsa.DSAPrivateKey)
+
+ params = key.parameters()
+ assert isinstance(params, dsa.DSAParameters)
+
+ assert key.x == int("00a535a8e1d0d91beafc8bee1d9b2a3a8de3311203", 16)
+ assert key.y == int(
+ "2b260ea97dc6a12ae932c640e7df3d8ff04a8a05a0324f8d5f1b23f15fa1"
+ "70ff3f42061124eff2586cb11b49a82dcdc1b90fc6a84fb10109cb67db5d"
+ "2da971aeaf17be5e37284563e4c64d9e5fc8480258b319f0de29d54d8350"
+ "70d9e287914d77df81491f4423b62da984eb3f45eb2a29fcea5dae525ac6"
+ "ab6bcce04bfdf5b6",
+ 16
+ )
+
+ assert params.p == int(
+ "00aa0930cc145825221caffa28ac2894196a27833de5ec21270791689420"
+ "7774a2e7b238b0d36f1b2499a2c2585083eb01432924418d867faa212dd1"
+ "071d4dceb2782794ad393cc08a4d4ada7f68d6e839a5fcd34b4e402d82cb"
+ "8a8cb40fec31911bf9bd360b034caacb4c5e947992573c9e90099c1b0f05"
+ "940cabe5d2de49a167",
+ 16
+ )
+
+ assert params.q == int("00adc0e869b36f0ac013a681fdf4d4899d69820451",
+ 16)
+
+ assert params.g == int(
+ "008c6b4589afa53a4d1048bfc346d1f386ca75521ccf72ddaa251286880e"
+ "e13201ff48890bbfc33d79bacaec71e7a778507bd5f1a66422e39415be03"
+ "e71141ba324f5b93131929182c88a9fa4062836066cebe74b5c6690c7d10"
+ "1106c240ab7ebd54e4e3301fd086ce6adac922fb2713a2b0887cba13b9bc"
+ "68ce5cfff241cd3246",
+ 16
+ )
+
+ @pytest.mark.parametrize(
+ ("key_file", "password"),
+ [
+ ("bad-oid-dsa-key.pem", None),
+ ]
+ )
+ def test_load_bad_oid_key(self, key_file, password, backend):
+ with raises_unsupported_algorithm(
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM
+ ):
+ load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", key_file),
+ lambda pemfile: load_pem_traditional_openssl_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
+
+ @pytest.mark.parametrize(
+ ("key_file", "password"),
+ [
+ ("bad-encryption-oid.pem", b"password"),
+ ]
+ )
+ def test_load_bad_encryption_oid_key(self, key_file, password, backend):
+ with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER):
+ load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "PKCS8", key_file),
+ lambda pemfile: load_pem_traditional_openssl_private_key(
+ pemfile.read().encode(), password, backend
+ )
+ )
diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py
index 6c3f4c95..a496459b 100644
--- a/tests/hazmat/primitives/utils.py
+++ b/tests/hazmat/primitives/utils.py
@@ -26,7 +26,7 @@ from cryptography.exceptions import (
from cryptography.hazmat.primitives import hashes, hmac
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.ciphers import Cipher
-from cryptography.hazmat.primitives.kdf.hkdf import HKDF
+from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from ...utils import load_vectors_from_file
@@ -347,15 +347,14 @@ def hkdf_extract_test(backend, algorithm, params):
def hkdf_expand_test(backend, algorithm, params):
- hkdf = HKDF(
+ hkdf = HKDFExpand(
algorithm,
int(params["l"]),
- salt=binascii.unhexlify(params["salt"]) or None,
info=binascii.unhexlify(params["info"]) or None,
backend=backend
)
- okm = hkdf._expand(binascii.unhexlify(params["prk"]))
+ okm = hkdf.derive(binascii.unhexlify(params["prk"]))
assert okm == binascii.unhexlify(params["okm"])
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 7a0b9e74..4673b49e 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -27,11 +27,11 @@ from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
import cryptography_vectors
from .utils import (
- check_backend_support, check_for_iface, load_cryptrec_vectors,
- load_fips_dsa_key_pair_vectors, load_fips_dsa_sig_vectors,
- load_fips_ecdsa_key_pair_vectors, load_fips_ecdsa_signing_vectors,
- load_hash_vectors, load_nist_vectors, load_pkcs1_vectors,
- load_rsa_nist_vectors, load_vectors_from_file,
+ check_backend_support, check_for_iface, der_encode_dsa_signature,
+ load_cryptrec_vectors, load_fips_dsa_key_pair_vectors,
+ load_fips_dsa_sig_vectors, load_fips_ecdsa_key_pair_vectors,
+ load_fips_ecdsa_signing_vectors, load_hash_vectors, load_nist_vectors,
+ load_pkcs1_vectors, load_rsa_nist_vectors, load_vectors_from_file,
raises_unsupported_algorithm, select_backends
)
@@ -125,6 +125,26 @@ def test_check_backend_support_no_backend():
check_backend_support(item)
+def test_der_encode_dsa_signature_values():
+ sig = der_encode_dsa_signature(1, 1)
+ assert sig == b"0\x06\x02\x01\x01\x02\x01\x01"
+
+ sig2 = der_encode_dsa_signature(
+ 1037234182290683143945502320610861668562885151617,
+ 559776156650501990899426031439030258256861634312
+ )
+ assert sig2 == (
+ b'0-\x02\x15\x00\xb5\xaf0xg\xfb\x8bT9\x00\x13\xccg\x02\r\xdf\x1f,\x0b'
+ b'\x81\x02\x14b\r;"\xabP1D\x0c>5\xea\xb6\xf4\x81)\x8f\x9e\x9f\x08'
+ )
+
+ sig3 = der_encode_dsa_signature(0, 0)
+ assert sig3 == b"0\x06\x02\x01\x00\x02\x01\x00"
+
+ sig4 = der_encode_dsa_signature(-1, 0)
+ assert sig4 == b"0\x06\x02\x01\xFF\x02\x01\x00"
+
+
def test_load_nist_vectors():
vector_data = textwrap.dedent("""
# CAVS 11.1
diff --git a/tests/utils.py b/tests/utils.py
index 60b6f5a2..5c0e2343 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -18,6 +18,9 @@ import collections
import re
from contextlib import contextmanager
+from pyasn1.codec.der import encoder
+from pyasn1.type import namedtype, univ
+
import pytest
import six
@@ -80,6 +83,20 @@ def raises_unsupported_algorithm(reason):
assert exc_info.value._reason is reason
+class _DSSSigValue(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('r', univ.Integer()),
+ namedtype.NamedType('s', univ.Integer())
+ )
+
+
+def der_encode_dsa_signature(r, s):
+ sig = _DSSSigValue()
+ sig.setComponentByName('r', r)
+ sig.setComponentByName('s', s)
+ return encoder.encode(sig)
+
+
def load_vectors_from_file(filename, loader):
with cryptography_vectors.open_vector_file(filename) as vector_file:
return loader(vector_file)
diff --git a/tox.ini b/tox.ini
index da959955..745fb37a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -7,6 +7,7 @@ deps =
coverage
iso8601
pretend
+ pyasn1
pytest
./vectors
commands =
@@ -15,6 +16,7 @@ commands =
[testenv:docs]
deps =
+ doc8
pyenchant
sphinx
sphinx_rtd_theme
@@ -25,6 +27,7 @@ commands =
sphinx-build -W -b latex -d {envtmpdir}/doctrees docs docs/_build/latex
sphinx-build -W -b doctest -d {envtmpdir}/doctrees docs docs/_build/html
sphinx-build -W -b spelling docs docs/_build/html
+ doc8 --allow-long-titles README.rst docs/
[testenv:docs-linkcheck]
deps =
@@ -44,7 +47,8 @@ deps =
flake8
flake8-import-order
pep8-naming
-commands = flake8 .
+commands =
+ flake8 .
[testenv:py3pep8]
basepython = python3.3
@@ -52,7 +56,8 @@ deps =
flake8
flake8-import-order
pep8-naming
-commands = flake8 .
+commands =
+ flake8 .
[flake8]
exclude = .tox,*.egg
diff --git a/vectors/cryptography_vectors/__about__.py b/vectors/cryptography_vectors/__about__.py
index ad61c1bc..bbacbf5d 100644
--- a/vectors/cryptography_vectors/__about__.py
+++ b/vectors/cryptography_vectors/__about__.py
@@ -22,7 +22,7 @@ __summary__ = "Test vectors for the cryptography package."
__uri__ = "https://github.com/pyca/cryptography"
-__version__ = "0.4.dev1"
+__version__ = "0.5.dev1"
__author__ = "The cryptography developers"
__email__ = "cryptography-dev@python.org"
diff --git a/vectors/cryptography_vectors/asymmetric/PKCS8/bad-encryption-oid.pem b/vectors/cryptography_vectors/asymmetric/PKCS8/bad-encryption-oid.pem
new file mode 100644
index 00000000..4ebcc12c
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/PKCS8/bad-encryption-oid.pem
@@ -0,0 +1,17 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICojAcBgoYYYYYYYYYYYYYMA4ECHK0M0+QuEL9AgIBIgSCAoDRq+KRY+0XP0tO
+lwBTzViiXSXoyNnKAZKt5r5K/fGNntv22g/1s/ZNCetrqsJDC5eMUPPacz06jFq/
+Ipsep4/OgjQ9UAOzXNrWEoNyrHnWDo7usgD3CW0mKyqER4+wG0ZdVMbt3N+CJHGB
+85jzRmQTfkdx1rSWeSx+XyswHn8ER4+hQ+omKWMVm7AFkjjmP/KmhUnLT98J8rhU
+ArQoFPHz/6HVkypFccNaPPNg6IA4aS2A+TU9vJYOaXSVfFB2yf99hfYYzC+ukmuU
+5Lun0cysK5s/5uSwDueUmDQKspnaNyiaMGDxvw8hilJc7vg0fGObfnbIpizhxJwq
+gKBfR7Zt0Hv8OYi1He4MehfMGdbHskztF+yQ40LplBGXQrvAqpU4zShga1BoQ98T
+0ekbBmqj7hg47VFsppXR7DKhx7G7rpMmdKbFhAZVCjae7rRGpUtD52cpFdPhMyAX
+huhMkoczwUW8B/rM4272lkHo6Br0yk/TQfTEGkvryflNVu6lniPTV151WV5U1M3o
+3G3a44eDyt7Ln+WSOpWtbPQMTrpKhur6WXgJvrpa/m02oOGdvOlDsoOCgavgQMWg
+7xKKL7620pHl7p7f/8tlE8q6vLXVvyNtAOgt/JAr2rgvrHaZSzDE0DwgCjBXEm+7
+cVMVNkHod7bLQefVanVtWqPzbmr8f7gKeuGwWSG9oew/lN2hxcLEPJHAQlnLgx3P
+0GdGjK9NvwA0EP2gYIeE4+UtSder7xQ7bVh25VB20R4TTIIs4aXXCVOoQPagnzaT
+6JLgl8FrvdfjHwIvmSOO1YMNmILBq000Q8WDqyErBDs4hsvtO6VQ4LeqJj6gClX3
+qeJNaJFu
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/vectors/cryptography_vectors/asymmetric/PKCS8/bad-oid-dsa-key.pem b/vectors/cryptography_vectors/asymmetric/PKCS8/bad-oid-dsa-key.pem
new file mode 100644
index 00000000..50d045be
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/PKCS8/bad-oid-dsa-key.pem
@@ -0,0 +1,9 @@
+-----BEGIN PRIVATE KEY-----
+MIIBTAIBADCCASwGByXXXXXXXXEwggEfAoGBAKoJMMwUWCUiHK/6KKwolBlqJ4M9
+5ewhJweRaJQgd3Si57I4sNNvGySZosJYUIPrAUMpJEGNhn+qIS3RBx1NzrJ4J5St
+OTzAik1K2n9o1ug5pfzTS05ALYLLioy0D+wxkRv5vTYLA0yqy0xelHmSVzyekAmc
+Gw8FlAyr5dLeSaFnAhUArcDoabNvCsATpoH99NSJnWmCBFECgYEAjGtFia+lOk0Q
+SL/DRtHzhsp1UhzPct2qJRKGiA7hMgH/SIkLv8M9ebrK7HHnp3hQe9XxpmQi45QV
+vgPnEUG6Mk9bkxMZKRgsiKn6QGKDYGbOvnS1xmkMfRARBsJAq369VOTjMB/Qhs5q
+2ski+ycTorCIfLoTubxozlz/8kHNMkYEFwIVAKU1qOHQ2Rvq/IvuHZsqOo3jMRID
+-----END PRIVATE KEY-----
diff --git a/vectors/cryptography_vectors/asymmetric/PKCS8/unenc-dsa-pkcs8.pem b/vectors/cryptography_vectors/asymmetric/PKCS8/unenc-dsa-pkcs8.pem
new file mode 100644
index 00000000..7b2099d3
--- /dev/null
+++ b/vectors/cryptography_vectors/asymmetric/PKCS8/unenc-dsa-pkcs8.pem
@@ -0,0 +1,9 @@
+-----BEGIN PRIVATE KEY-----
+MIIBTAIBADCCASwGByqGSM44BAEwggEfAoGBAKoJMMwUWCUiHK/6KKwolBlqJ4M9
+5ewhJweRaJQgd3Si57I4sNNvGySZosJYUIPrAUMpJEGNhn+qIS3RBx1NzrJ4J5St
+OTzAik1K2n9o1ug5pfzTS05ALYLLioy0D+wxkRv5vTYLA0yqy0xelHmSVzyekAmc
+Gw8FlAyr5dLeSaFnAhUArcDoabNvCsATpoH99NSJnWmCBFECgYEAjGtFia+lOk0Q
+SL/DRtHzhsp1UhzPct2qJRKGiA7hMgH/SIkLv8M9ebrK7HHnp3hQe9XxpmQi45QV
+vgPnEUG6Mk9bkxMZKRgsiKn6QGKDYGbOvnS1xmkMfRARBsJAq369VOTjMB/Qhs5q
+2ski+ycTorCIfLoTubxozlz/8kHNMkYEFwIVAKU1qOHQ2Rvq/IvuHZsqOo3jMRID
+-----END PRIVATE KEY-----