aboutsummaryrefslogtreecommitdiffstats
path: root/cryptography/hazmat
diff options
context:
space:
mode:
Diffstat (limited to 'cryptography/hazmat')
-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
39 files changed, 1492 insertions, 252 deletions
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.")