aboutsummaryrefslogtreecommitdiffstats
path: root/cryptography/hazmat
diff options
context:
space:
mode:
Diffstat (limited to 'cryptography/hazmat')
-rw-r--r--cryptography/hazmat/backends/__init__.py4
-rw-r--r--cryptography/hazmat/backends/commoncrypto/backend.py13
-rw-r--r--cryptography/hazmat/backends/interfaces.py24
-rw-r--r--cryptography/hazmat/backends/multibackend.py37
-rw-r--r--cryptography/hazmat/backends/openssl/backend.py373
-rw-r--r--cryptography/hazmat/bindings/commoncrypto/binding.py4
-rw-r--r--cryptography/hazmat/bindings/openssl/aes.py28
-rw-r--r--cryptography/hazmat/bindings/openssl/binding.py2
-rw-r--r--cryptography/hazmat/bindings/openssl/bio.py5
-rw-r--r--cryptography/hazmat/bindings/openssl/dsa.py3
-rw-r--r--cryptography/hazmat/bindings/openssl/ec.py21
-rw-r--r--cryptography/hazmat/bindings/openssl/err.py44
-rw-r--r--cryptography/hazmat/bindings/openssl/evp.py27
-rw-r--r--cryptography/hazmat/bindings/openssl/hmac.py6
-rw-r--r--cryptography/hazmat/bindings/openssl/nid.py163
-rw-r--r--cryptography/hazmat/bindings/openssl/osrandom_engine.py13
-rw-r--r--cryptography/hazmat/bindings/openssl/pem.py4
-rw-r--r--cryptography/hazmat/bindings/openssl/rsa.py25
-rw-r--r--cryptography/hazmat/bindings/openssl/ssl.py55
-rw-r--r--cryptography/hazmat/bindings/openssl/x509.py20
-rw-r--r--cryptography/hazmat/primitives/asymmetric/padding.py22
-rw-r--r--cryptography/hazmat/primitives/asymmetric/rsa.py22
-rw-r--r--cryptography/hazmat/primitives/interfaces.py129
-rw-r--r--cryptography/hazmat/primitives/kdf/pbkdf2.py4
-rw-r--r--cryptography/hazmat/primitives/twofactor/__init__.py0
-rw-r--r--cryptography/hazmat/primitives/twofactor/hotp.py62
-rw-r--r--cryptography/hazmat/primitives/twofactor/totp.py32
27 files changed, 1008 insertions, 134 deletions
diff --git a/cryptography/hazmat/backends/__init__.py b/cryptography/hazmat/backends/__init__.py
index 41d260a8..406b37e5 100644
--- a/cryptography/hazmat/backends/__init__.py
+++ b/cryptography/hazmat/backends/__init__.py
@@ -17,12 +17,14 @@ from cryptography.hazmat.bindings.commoncrypto.binding import (
Binding as CommonCryptoBinding
)
-_ALL_BACKENDS = [openssl.backend]
+_ALL_BACKENDS = []
if CommonCryptoBinding.is_available():
from cryptography.hazmat.backends import commoncrypto
_ALL_BACKENDS.append(commoncrypto.backend)
+_ALL_BACKENDS.append(openssl.backend)
+
_default_backend = MultiBackend(_ALL_BACKENDS)
diff --git a/cryptography/hazmat/backends/commoncrypto/backend.py b/cryptography/hazmat/backends/commoncrypto/backend.py
index 5c08a356..53228b31 100644
--- a/cryptography/hazmat/backends/commoncrypto/backend.py
+++ b/cryptography/hazmat/backends/commoncrypto/backend.py
@@ -17,7 +17,7 @@ from collections import namedtuple
from cryptography import utils
from cryptography.exceptions import (
- UnsupportedAlgorithm, InvalidTag, InternalError
+ InvalidTag, InternalError, UnsupportedCipher, UnsupportedHash
)
from cryptography.hazmat.backends.interfaces import (
HashBackend, HMACBackend, CipherBackend, PBKDF2HMACBackend
@@ -202,7 +202,8 @@ class Backend(object):
(CBC, self._lib.kCCModeCBC),
(ECB, self._lib.kCCModeECB),
(CFB, self._lib.kCCModeCFB),
- (OFB, self._lib.kCCModeOFB)
+ (OFB, self._lib.kCCModeOFB),
+ (CTR, self._lib.kCCModeCTR)
]:
self._register_cipher_adapter(
CAST5,
@@ -272,7 +273,7 @@ class _CipherContext(object):
try:
cipher_enum, mode_enum = registry[type(cipher), type(mode)]
except KeyError:
- raise UnsupportedAlgorithm(
+ raise UnsupportedCipher(
"cipher {0} in {1} mode is not supported "
"by this backend".format(
cipher.name, mode.name if mode else mode)
@@ -345,7 +346,7 @@ class _GCMCipherContext(object):
try:
cipher_enum, mode_enum = registry[type(cipher), type(mode)]
except KeyError:
- raise UnsupportedAlgorithm(
+ raise UnsupportedCipher(
"cipher {0} in {1} mode is not supported "
"by this backend".format(
cipher.name, mode.name if mode else mode)
@@ -419,7 +420,7 @@ class _HashContext(object):
try:
methods = self._backend._hash_mapping[self.algorithm.name]
except KeyError:
- raise UnsupportedAlgorithm(
+ raise UnsupportedHash(
"{0} is not a supported hash on this backend".format(
algorithm.name)
)
@@ -462,7 +463,7 @@ class _HMACContext(object):
try:
alg = self._backend._supported_hmac_algorithms[algorithm.name]
except KeyError:
- raise UnsupportedAlgorithm(
+ raise UnsupportedHash(
"{0} is not a supported HMAC hash on this backend".format(
algorithm.name)
)
diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py
index b867f26a..da41532d 100644
--- a/cryptography/hazmat/backends/interfaces.py
+++ b/cryptography/hazmat/backends/interfaces.py
@@ -90,3 +90,27 @@ class RSABackend(six.with_metaclass(abc.ABCMeta)):
Generate an RSAPrivateKey instance with public_exponent and a modulus
of key_size bits.
"""
+
+ @abc.abstractmethod
+ def create_rsa_signature_ctx(self, private_key, padding, algorithm):
+ """
+ Returns an object conforming to the AsymmetricSignatureContext
+ interface.
+ """
+
+ @abc.abstractmethod
+ def create_rsa_verification_ctx(self, public_key, signature, padding,
+ algorithm):
+ """
+ Returns an object conforming to the AsymmetricVerificationContext
+ interface.
+ """
+
+
+class OpenSSLSerializationBackend(six.with_metaclass(abc.ABCMeta)):
+ @abc.abstractmethod
+ def load_openssl_pem_private_key(self, data, password):
+ """
+ Load a private key from PEM encoded data, using password if the data
+ is encrypted.
+ """
diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py
index 4de02026..cca82a59 100644
--- a/cryptography/hazmat/backends/multibackend.py
+++ b/cryptography/hazmat/backends/multibackend.py
@@ -14,7 +14,9 @@
from __future__ import absolute_import, division, print_function
from cryptography import utils
-from cryptography.exceptions import UnsupportedAlgorithm
+from cryptography.exceptions import (
+ UnsupportedAlgorithm, UnsupportedCipher, UnsupportedHash
+)
from cryptography.hazmat.backends.interfaces import (
CipherBackend, HashBackend, HMACBackend, PBKDF2HMACBackend, RSABackend
)
@@ -24,6 +26,7 @@ from cryptography.hazmat.backends.interfaces import (
@utils.register_interface(HashBackend)
@utils.register_interface(HMACBackend)
@utils.register_interface(PBKDF2HMACBackend)
+@utils.register_interface(RSABackend)
class MultiBackend(object):
name = "multibackend"
@@ -45,17 +48,17 @@ class MultiBackend(object):
for b in self._filtered_backends(CipherBackend):
try:
return b.create_symmetric_encryption_ctx(algorithm, mode)
- except UnsupportedAlgorithm:
+ except UnsupportedCipher:
pass
- raise UnsupportedAlgorithm
+ raise UnsupportedCipher
def create_symmetric_decryption_ctx(self, algorithm, mode):
for b in self._filtered_backends(CipherBackend):
try:
return b.create_symmetric_decryption_ctx(algorithm, mode)
- except UnsupportedAlgorithm:
+ except UnsupportedCipher:
pass
- raise UnsupportedAlgorithm
+ raise UnsupportedCipher
def hash_supported(self, algorithm):
return any(
@@ -67,9 +70,9 @@ class MultiBackend(object):
for b in self._filtered_backends(HashBackend):
try:
return b.create_hash_ctx(algorithm)
- except UnsupportedAlgorithm:
+ except UnsupportedHash:
pass
- raise UnsupportedAlgorithm
+ raise UnsupportedHash
def hmac_supported(self, algorithm):
return any(
@@ -81,9 +84,9 @@ class MultiBackend(object):
for b in self._filtered_backends(HMACBackend):
try:
return b.create_hmac_ctx(key, algorithm)
- except UnsupportedAlgorithm:
+ except UnsupportedHash:
pass
- raise UnsupportedAlgorithm
+ raise UnsupportedHash
def pbkdf2_hmac_supported(self, algorithm):
return any(
@@ -98,11 +101,23 @@ class MultiBackend(object):
return b.derive_pbkdf2_hmac(
algorithm, length, salt, iterations, key_material
)
- except UnsupportedAlgorithm:
+ except UnsupportedHash:
pass
- raise UnsupportedAlgorithm
+ raise UnsupportedHash
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
+
+ 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
+
+ def create_rsa_verification_ctx(self, public_key, signature, padding,
+ algorithm):
+ for b in self._filtered_backends(RSABackend):
+ return b.create_rsa_verification_ctx(public_key, signature,
+ padding, algorithm)
+ raise UnsupportedAlgorithm
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 8a4aeac5..b4625aae 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -13,11 +13,13 @@
from __future__ import absolute_import, division, print_function
+import collections
import itertools
from cryptography import utils
from cryptography.exceptions import (
- UnsupportedAlgorithm, InvalidTag, InternalError
+ InvalidTag, InternalError, AlreadyFinalized, UnsupportedCipher,
+ UnsupportedHash, UnsupportedPadding, InvalidSignature
)
from cryptography.hazmat.backends.interfaces import (
CipherBackend, HashBackend, HMACBackend, PBKDF2HMACBackend, RSABackend
@@ -33,6 +35,10 @@ from cryptography.hazmat.primitives.ciphers.modes import (
)
+_OpenSSLError = collections.namedtuple("_OpenSSLError",
+ ["code", "lib", "func", "reason"])
+
+
@utils.register_interface(CipherBackend)
@utils.register_interface(HashBackend)
@utils.register_interface(HMACBackend)
@@ -205,7 +211,7 @@ class Backend(object):
assert res == 1
else:
if not isinstance(algorithm, hashes.SHA1):
- raise UnsupportedAlgorithm(
+ raise UnsupportedHash(
"This version of OpenSSL only supports PBKDF2HMAC with "
"SHA1"
)
@@ -227,43 +233,25 @@ class Backend(object):
self._lib.ERR_error_string_n(code, err_buf, 256)
return self._ffi.string(err_buf, 256)[:]
- def _handle_error(self, mode):
- code = self._lib.ERR_get_error()
- if not code and isinstance(mode, GCM):
- raise InvalidTag
- assert code != 0
-
- # consume any remaining errors on the stack
- ignored_code = None
- while ignored_code != 0:
- ignored_code = self._lib.ERR_get_error()
-
- # raise the first error we found
- return self._handle_error_code(code)
-
- def _handle_error_code(self, code):
- lib = self._lib.ERR_GET_LIB(code)
- func = self._lib.ERR_GET_FUNC(code)
- reason = self._lib.ERR_GET_REASON(code)
-
- if lib == self._lib.ERR_LIB_EVP:
- if func == self._lib.EVP_F_EVP_ENCRYPTFINAL_EX:
- if reason == self._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
- raise ValueError(
- "The length of the provided data is not a multiple of "
- "the block length"
- )
- elif func == self._lib.EVP_F_EVP_DECRYPTFINAL_EX:
- if reason == self._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
- raise ValueError(
- "The length of the provided data is not a multiple of "
- "the block length"
- )
-
- raise InternalError(
+ def _consume_errors(self):
+ errors = []
+ while True:
+ code = self._lib.ERR_get_error()
+ if code == 0:
+ break
+
+ lib = self._lib.ERR_GET_LIB(code)
+ func = self._lib.ERR_GET_FUNC(code)
+ reason = self._lib.ERR_GET_REASON(code)
+
+ errors.append(_OpenSSLError(code, lib, func, reason))
+ return errors
+
+ def _unknown_error(self, error):
+ return InternalError(
"Unknown error code {0} from OpenSSL, "
"you should probably file a bug. {1}".format(
- code, self._err_string(code)
+ error.code, self._err_string(error.code)
)
)
@@ -274,6 +262,20 @@ class Backend(object):
self._lib.OPENSSL_free(hex_cdata)
return int(hex_str, 16)
+ def _int_to_bn(self, num):
+ """
+ Converts a python integer to a BIGNUM. The returned BIGNUM will not
+ be garbage collected (to support adding them to structs that take
+ ownership of the object). Be sure to register it for GC if it will
+ be discarded after use.
+ """
+ hex_num = hex(num).rstrip("L").lstrip("0x").encode("ascii") or b"0"
+ bn_ptr = self._ffi.new("BIGNUM **")
+ res = self._lib.BN_hex2bn(bn_ptr, hex_num)
+ assert res != 0
+ assert bn_ptr[0] != self._ffi.NULL
+ 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")
@@ -284,18 +286,15 @@ class Backend(object):
if key_size < 512:
raise ValueError("key_size must be at least 512-bits")
- ctx = backend._lib.RSA_new()
- ctx = backend._ffi.gc(ctx, backend._lib.RSA_free)
-
- bn = backend._lib.BN_new()
- assert bn != self._ffi.NULL
- bn = backend._ffi.gc(bn, backend._lib.BN_free)
+ ctx = self._lib.RSA_new()
+ assert ctx != self._ffi.NULL
+ ctx = self._ffi.gc(ctx, self._lib.RSA_free)
- res = backend._lib.BN_set_word(bn, public_exponent)
- assert res == 1
+ bn = self._int_to_bn(public_exponent)
+ bn = self._ffi.gc(bn, self._lib.BN_free)
- res = backend._lib.RSA_generate_key_ex(
- ctx, key_size, bn, backend._ffi.NULL
+ res = self._lib.RSA_generate_key_ex(
+ ctx, key_size, bn, self._ffi.NULL
)
assert res == 1
@@ -310,6 +309,36 @@ class Backend(object):
modulus=self._bn_to_int(ctx.n),
)
+ def _rsa_cdata_from_private_key(self, private_key):
+ ctx = self._lib.RSA_new()
+ assert ctx != self._ffi.NULL
+ ctx = self._ffi.gc(ctx, self._lib.RSA_free)
+ ctx.p = self._int_to_bn(private_key.p)
+ ctx.q = self._int_to_bn(private_key.q)
+ ctx.d = self._int_to_bn(private_key.d)
+ ctx.e = self._int_to_bn(private_key.e)
+ ctx.n = self._int_to_bn(private_key.n)
+ ctx.dmp1 = self._int_to_bn(private_key.dmp1)
+ ctx.dmq1 = self._int_to_bn(private_key.dmq1)
+ ctx.iqmp = self._int_to_bn(private_key.iqmp)
+ return ctx
+
+ def _rsa_cdata_from_public_key(self, public_key):
+ ctx = self._lib.RSA_new()
+ assert ctx != self._ffi.NULL
+ ctx = self._ffi.gc(ctx, self._lib.RSA_free)
+ ctx.e = self._int_to_bn(public_key.e)
+ ctx.n = self._int_to_bn(public_key.n)
+ return ctx
+
+ def create_rsa_signature_ctx(self, private_key, padding, algorithm):
+ return _RSASignatureContext(self, private_key, padding, algorithm)
+
+ def create_rsa_verification_ctx(self, public_key, signature, padding,
+ algorithm):
+ return _RSAVerificationContext(self, public_key, signature, padding,
+ algorithm)
+
class GetCipherByName(object):
def __init__(self, fmt):
@@ -348,7 +377,7 @@ class _CipherContext(object):
try:
adapter = registry[type(cipher), type(mode)]
except KeyError:
- raise UnsupportedAlgorithm(
+ raise UnsupportedCipher(
"cipher {0} in {1} mode is not supported "
"by this backend".format(
cipher.name, mode.name if mode else mode)
@@ -356,7 +385,7 @@ class _CipherContext(object):
evp_cipher = adapter(self._backend, cipher, mode)
if evp_cipher == self._backend._ffi.NULL:
- raise UnsupportedAlgorithm(
+ raise UnsupportedCipher(
"cipher {0} in {1} mode is not supported "
"by this backend".format(
cipher.name, mode.name if mode else mode)
@@ -409,6 +438,15 @@ class _CipherContext(object):
self._ctx = ctx
def update(self, data):
+ # OpenSSL 0.9.8e has an assertion in its EVP code that causes it
+ # to SIGABRT if you call update with an empty byte string. This can be
+ # removed when we drop support for 0.9.8e (CentOS/RHEL 5). This branch
+ # should be taken only when length is zero and mode is not GCM because
+ # AES GCM can return improper tag values if you don't call update
+ # with empty plaintext when authenticating AAD for ...reasons.
+ if len(data) == 0 and not isinstance(self._mode, GCM):
+ return b""
+
buf = self._backend._ffi.new("unsigned char[]",
len(data) + self._block_size - 1)
outlen = self._backend._ffi.new("int *")
@@ -422,7 +460,28 @@ class _CipherContext(object):
outlen = self._backend._ffi.new("int *")
res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen)
if res == 0:
- self._backend._handle_error(self._mode)
+ errors = self._backend._consume_errors()
+
+ if not errors and isinstance(self._mode, GCM):
+ raise InvalidTag
+
+ assert errors
+
+ if errors[0][1:] == (
+ self._backend._lib.ERR_LIB_EVP,
+ self._backend._lib.EVP_F_EVP_ENCRYPTFINAL_EX,
+ self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
+ ) or errors[0][1:] == (
+ self._backend._lib.ERR_LIB_EVP,
+ self._backend._lib.EVP_F_EVP_DECRYPTFINAL_EX,
+ self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
+ ):
+ raise ValueError(
+ "The length of the provided data is not a multiple of "
+ "the block length."
+ )
+ else:
+ raise self._backend._unknown_error(errors[0])
if (isinstance(self._mode, GCM) and
self._operation == self._ENCRYPT):
@@ -467,7 +526,7 @@ class _HashContext(object):
evp_md = self._backend._lib.EVP_get_digestbyname(
algorithm.name.encode("ascii"))
if evp_md == self._backend._ffi.NULL:
- raise UnsupportedAlgorithm(
+ raise UnsupportedHash(
"{0} is not a supported hash on this backend".format(
algorithm.name)
)
@@ -492,13 +551,14 @@ class _HashContext(object):
def finalize(self):
buf = self._backend._ffi.new("unsigned char[]",
- self.algorithm.digest_size)
- res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf,
- self._backend._ffi.NULL)
+ self._backend._lib.EVP_MAX_MD_SIZE)
+ outlen = self._backend._ffi.new("unsigned int *")
+ res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen)
assert res != 0
+ assert outlen[0] == self.algorithm.digest_size
res = self._backend._lib.EVP_MD_CTX_cleanup(self._ctx)
assert res == 1
- return self._backend._ffi.buffer(buf)[:]
+ return self._backend._ffi.buffer(buf)[:outlen[0]]
@utils.register_interface(interfaces.HashContext)
@@ -516,7 +576,7 @@ class _HMACContext(object):
evp_md = self._backend._lib.EVP_get_digestbyname(
algorithm.name.encode('ascii'))
if evp_md == self._backend._ffi.NULL:
- raise UnsupportedAlgorithm(
+ raise UnsupportedHash(
"{0} is not a supported hash on this backend".format(
algorithm.name)
)
@@ -550,15 +610,208 @@ class _HMACContext(object):
def finalize(self):
buf = self._backend._ffi.new("unsigned char[]",
- self.algorithm.digest_size)
- buflen = self._backend._ffi.new("unsigned int *",
- self.algorithm.digest_size)
+ self._backend._lib.EVP_MAX_MD_SIZE)
+ outlen = self._backend._ffi.new("unsigned int *")
res = self._backend._lib.Cryptography_HMAC_Final(
- self._ctx, buf, buflen
+ self._ctx, buf, outlen
)
assert res != 0
+ assert outlen[0] == self.algorithm.digest_size
self._backend._lib.HMAC_CTX_cleanup(self._ctx)
+ return self._backend._ffi.buffer(buf)[:outlen[0]]
+
+
+@utils.register_interface(interfaces.AsymmetricSignatureContext)
+class _RSASignatureContext(object):
+ def __init__(self, backend, private_key, padding, algorithm):
+ self._backend = backend
+ self._private_key = private_key
+ if not isinstance(padding, interfaces.AsymmetricPadding):
+ raise TypeError(
+ "Expected provider of interfaces.AsymmetricPadding")
+
+ if padding.name == "EMSA-PKCS1-v1_5":
+ if self._backend._lib.Cryptography_HAS_PKEY_CTX:
+ self._finalize_method = self._finalize_pkey_ctx
+ self._padding_enum = self._backend._lib.RSA_PKCS1_PADDING
+ else:
+ self._finalize_method = self._finalize_pkcs1
+ else:
+ raise UnsupportedPadding(
+ "{0} is not supported by this backend".format(padding.name)
+ )
+
+ self._padding = padding
+ self._algorithm = algorithm
+ self._hash_ctx = _HashContext(backend, self._algorithm)
+
+ 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._lib.EVP_PKEY_new()
+ assert evp_pkey != self._backend._ffi.NULL
+ evp_pkey = backend._ffi.gc(evp_pkey, backend._lib.EVP_PKEY_free)
+ rsa_cdata = backend._rsa_cdata_from_private_key(self._private_key)
+ res = self._backend._lib.RSA_blinding_on(
+ rsa_cdata, self._backend._ffi.NULL)
+ assert res == 1
+ res = self._backend._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata)
+ assert res == 1
+ evp_md = self._backend._lib.EVP_get_digestbyname(
+ self._algorithm.name.encode("ascii"))
+ assert evp_md != self._backend._ffi.NULL
+ pkey_size = self._backend._lib.EVP_PKEY_size(evp_pkey)
+ assert pkey_size > 0
+
+ return self._finalize_method(evp_pkey, pkey_size, rsa_cdata, evp_md)
+
+ def _finalize_pkey_ctx(self, evp_pkey, pkey_size, rsa_cdata, evp_md):
+ pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new(
+ evp_pkey, self._backend._ffi.NULL
+ )
+ assert pkey_ctx != self._backend._ffi.NULL
+ res = self._backend._lib.EVP_PKEY_sign_init(pkey_ctx)
+ assert res == 1
+ res = self._backend._lib.EVP_PKEY_CTX_set_signature_md(
+ pkey_ctx, evp_md)
+ assert res > 0
+
+ res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding(
+ pkey_ctx, self._padding_enum)
+ 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,
+ self._backend._ffi.NULL,
+ buflen,
+ data_to_sign,
+ len(data_to_sign)
+ )
+ assert res == 1
+ buf = self._backend._ffi.new("unsigned char[]", buflen[0])
+ res = self._backend._lib.EVP_PKEY_sign(
+ pkey_ctx, buf, buflen, data_to_sign, len(data_to_sign))
+ assert res == 1
return self._backend._ffi.buffer(buf)[:]
+ def _finalize_pkcs1(self, evp_pkey, pkey_size, rsa_cdata, evp_md):
+ 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,
+ sig_buf,
+ sig_len,
+ evp_pkey
+ )
+ self._hash_ctx.finalize()
+ self._hash_ctx = None
+ assert res == 1
+ return self._backend._ffi.buffer(sig_buf)[:sig_len[0]]
+
+
+@utils.register_interface(interfaces.AsymmetricVerificationContext)
+class _RSAVerificationContext(object):
+ def __init__(self, backend, public_key, signature, padding, algorithm):
+ self._backend = backend
+ self._public_key = public_key
+ self._signature = signature
+ if not isinstance(padding, interfaces.AsymmetricPadding):
+ raise TypeError(
+ "Expected provider of interfaces.AsymmetricPadding")
+
+ if padding.name == "EMSA-PKCS1-v1_5":
+ if self._backend._lib.Cryptography_HAS_PKEY_CTX:
+ self._verify_method = self._verify_pkey_ctx
+ self._padding_enum = self._backend._lib.RSA_PKCS1_PADDING
+ else:
+ self._verify_method = self._verify_pkcs1
+ else:
+ raise UnsupportedPadding
+
+ self._padding = padding
+ self._algorithm = algorithm
+ self._hash_ctx = _HashContext(backend, self._algorithm)
+
+ 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._lib.EVP_PKEY_new()
+ assert evp_pkey != self._backend._ffi.NULL
+ evp_pkey = backend._ffi.gc(evp_pkey, backend._lib.EVP_PKEY_free)
+ rsa_cdata = backend._rsa_cdata_from_public_key(self._public_key)
+ res = self._backend._lib.RSA_blinding_on(
+ rsa_cdata, self._backend._ffi.NULL)
+ assert res == 1
+ res = self._backend._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata)
+ assert res == 1
+ evp_md = self._backend._lib.EVP_get_digestbyname(
+ self._algorithm.name.encode("ascii"))
+ assert evp_md != self._backend._ffi.NULL
+
+ self._verify_method(rsa_cdata, evp_pkey, evp_md)
+
+ def _verify_pkey_ctx(self, rsa_cdata, evp_pkey, evp_md):
+ pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new(
+ evp_pkey, self._backend._ffi.NULL
+ )
+ assert pkey_ctx != self._backend._ffi.NULL
+ res = self._backend._lib.EVP_PKEY_verify_init(pkey_ctx)
+ assert res == 1
+ res = self._backend._lib.EVP_PKEY_CTX_set_signature_md(
+ pkey_ctx, evp_md)
+ assert res > 0
+
+ res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding(
+ pkey_ctx, self._padding_enum)
+ 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,
+ len(self._signature),
+ data_to_verify,
+ len(data_to_verify)
+ )
+ # 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.
+ assert res >= 0
+ if res == 0:
+ assert self._backend._consume_errors()
+ raise InvalidSignature
+
+ def _verify_pkcs1(self, rsa_cdata, evp_pkey, evp_md):
+ res = self._backend._lib.EVP_VerifyFinal(
+ self._hash_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.
+ assert res >= 0
+ if res == 0:
+ assert self._backend._consume_errors()
+ raise InvalidSignature
+
backend = Backend()
diff --git a/cryptography/hazmat/bindings/commoncrypto/binding.py b/cryptography/hazmat/bindings/commoncrypto/binding.py
index 45c0eaad..ee809425 100644
--- a/cryptography/hazmat/bindings/commoncrypto/binding.py
+++ b/cryptography/hazmat/bindings/commoncrypto/binding.py
@@ -14,6 +14,7 @@
from __future__ import absolute_import, division, print_function
import sys
+import platform
from cryptography.hazmat.bindings.utils import build_ffi
@@ -46,4 +47,5 @@ class Binding(object):
@classmethod
def is_available(cls):
- return sys.platform == "darwin"
+ return sys.platform == "darwin" and list(map(
+ int, platform.mac_ver()[0].split("."))) >= [10, 8, 0]
diff --git a/cryptography/hazmat/bindings/openssl/aes.py b/cryptography/hazmat/bindings/openssl/aes.py
index 6cbcd577..95ed5271 100644
--- a/cryptography/hazmat/bindings/openssl/aes.py
+++ b/cryptography/hazmat/bindings/openssl/aes.py
@@ -16,6 +16,8 @@ INCLUDES = """
"""
TYPES = """
+static const int Cryptography_HAS_AES_WRAP;
+
struct aes_key_st {
...;
};
@@ -25,16 +27,34 @@ 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 *);
+"""
+
+MACROS = """
+/* these can be moved back to FUNCTIONS once we drop support for 0.9.8h.
+ This should be when we drop RHEL/CentOS 5, which is on 0.9.8e. */
int AES_wrap_key(AES_KEY *, const unsigned char *, unsigned char *,
const unsigned char *, unsigned int);
int AES_unwrap_key(AES_KEY *, const unsigned char *, unsigned char *,
const unsigned char *, unsigned int);
"""
-MACROS = """
-"""
-
CUSTOMIZATIONS = """
+// OpenSSL 0.9.8h+
+#if OPENSSL_VERSION_NUMBER >= 0x0090808fL
+static const long Cryptography_HAS_AES_WRAP = 1;
+#else
+static const long Cryptography_HAS_AES_WRAP = 0;
+int (*AES_wrap_key)(AES_KEY *, const unsigned char *, unsigned char *,
+ const unsigned char *, unsigned int) = NULL;
+int (*AES_unwrap_key)(AES_KEY *, const unsigned char *, unsigned char *,
+ const unsigned char *, unsigned int) = NULL;
+#endif
+
"""
-CONDITIONAL_NAMES = {}
+CONDITIONAL_NAMES = {
+ "Cryptography_HAS_AES_WRAP": [
+ "AES_wrap_key",
+ "AES_unwrap_key",
+ ],
+}
diff --git a/cryptography/hazmat/bindings/openssl/binding.py b/cryptography/hazmat/bindings/openssl/binding.py
index 714ecc07..0469a1ea 100644
--- a/cryptography/hazmat/bindings/openssl/binding.py
+++ b/cryptography/hazmat/bindings/openssl/binding.py
@@ -98,7 +98,7 @@ class Binding(object):
_OSX_PRE_INCLUDE, _OSX_POST_INCLUDE,
libraries)
res = cls.lib.Cryptography_add_osrandom_engine()
- assert res == 1
+ assert res != 0
@classmethod
def is_available(cls):
diff --git a/cryptography/hazmat/bindings/openssl/bio.py b/cryptography/hazmat/bindings/openssl/bio.py
index 279ad223..28172689 100644
--- a/cryptography/hazmat/bindings/openssl/bio.py
+++ b/cryptography/hazmat/bindings/openssl/bio.py
@@ -105,7 +105,6 @@ BIO *BIO_push(BIO *, BIO *);
BIO *BIO_pop(BIO *);
BIO *BIO_next(BIO *);
BIO *BIO_find_type(BIO *, int);
-int BIO_method_type(const BIO *);
BIO_METHOD *BIO_s_mem(void);
BIO *BIO_new_mem_buf(void *, int);
BIO_METHOD *BIO_s_file(void);
@@ -168,6 +167,10 @@ long BIO_set_read_buffer_size(BIO *, long);
long BIO_set_write_buffer_size(BIO *, long);
long BIO_set_buffer_size(BIO *, long);
long BIO_set_buffer_read_data(BIO *, void *, long);
+
+/* The following was a macro in 0.9.8e. Once we drop support for RHEL/CentOS 5
+ we should move this back to FUNCTIONS. */
+int BIO_method_type(const BIO *);
"""
CUSTOMIZATIONS = """
diff --git a/cryptography/hazmat/bindings/openssl/dsa.py b/cryptography/hazmat/bindings/openssl/dsa.py
index 609a33bf..664296d3 100644
--- a/cryptography/hazmat/bindings/openssl/dsa.py
+++ b/cryptography/hazmat/bindings/openssl/dsa.py
@@ -35,10 +35,13 @@ FUNCTIONS = """
DSA *DSA_generate_parameters(int, unsigned char *, int, int *, unsigned long *,
void (*)(int, int, void *), void *);
int DSA_generate_key(DSA *);
+DSA *DSA_new(void);
void DSA_free(DSA *);
"""
MACROS = """
+int DSA_generate_parameters_ex(DSA *, int, unsigned char *, int,
+ int *, unsigned long *, BN_GENCB *);
"""
CUSTOMIZATIONS = """
diff --git a/cryptography/hazmat/bindings/openssl/ec.py b/cryptography/hazmat/bindings/openssl/ec.py
index 39403ff2..9d6f7cb9 100644
--- a/cryptography/hazmat/bindings/openssl/ec.py
+++ b/cryptography/hazmat/bindings/openssl/ec.py
@@ -23,14 +23,10 @@ TYPES = """
static const int Cryptography_HAS_EC;
typedef ... EC_KEY;
-
-static const int NID_X9_62_prime192v1;
-static const int NID_X9_62_prime192v2;
-static const int NID_X9_62_prime192v3;
-static const int NID_X9_62_prime239v1;
-static const int NID_X9_62_prime239v2;
-static const int NID_X9_62_prime239v3;
-static const int NID_X9_62_prime256v1;
+typedef struct {
+ int nid;
+ const char *comment;
+} EC_builtin_curve;
"""
FUNCTIONS = """
@@ -39,14 +35,22 @@ FUNCTIONS = """
MACROS = """
EC_KEY *EC_KEY_new_by_curve_name(int);
void EC_KEY_free(EC_KEY *);
+
+size_t EC_get_builtin_curves(EC_builtin_curve *, size_t);
+
"""
CUSTOMIZATIONS = """
#ifdef OPENSSL_NO_EC
static const long Cryptography_HAS_EC = 0;
typedef void EC_KEY;
+typedef struct {
+ int nid;
+ const char *comment;
+} EC_builtin_curve;
EC_KEY* (*EC_KEY_new_by_curve_name)(int) = NULL;
void (*EC_KEY_free)(EC_KEY *) = NULL;
+size_t (*EC_get_builtin_curves)(EC_builtin_curve *, size_t) = NULL;
#else
static const long Cryptography_HAS_EC = 1;
#endif
@@ -56,5 +60,6 @@ CONDITIONAL_NAMES = {
"Cryptography_HAS_EC": [
"EC_KEY_new_by_curve_name",
"EC_KEY_free",
+ "EC_get_builtin_curves",
],
}
diff --git a/cryptography/hazmat/bindings/openssl/err.py b/cryptography/hazmat/bindings/openssl/err.py
index ddb60ef7..f21d98b6 100644
--- a/cryptography/hazmat/bindings/openssl/err.py
+++ b/cryptography/hazmat/bindings/openssl/err.py
@@ -17,6 +17,7 @@ INCLUDES = """
TYPES = """
static const int Cryptography_HAS_REMOVE_THREAD_STATE;
+static const int Cryptography_HAS_098H_ERROR_CODES;
struct ERR_string_data_st {
unsigned long error;
@@ -50,8 +51,6 @@ static const int ASN1_F_ASN1_TYPE_GET_OCTETSTRING;
static const int ASN1_F_ASN1_UNPACK_STRING;
static const int ASN1_F_ASN1_UTCTIME_SET;
static const int ASN1_F_ASN1_VERIFY;
-static const int ASN1_F_B64_READ_ASN1;
-static const int ASN1_F_B64_WRITE_ASN1;
static const int ASN1_F_BITSTR_CB;
static const int ASN1_F_BN_TO_ASN1_ENUMERATED;
static const int ASN1_F_BN_TO_ASN1_INTEGER;
@@ -71,8 +70,6 @@ static const int ASN1_F_LONG_C2I;
static const int ASN1_F_OID_MODULE_INIT;
static const int ASN1_F_PARSE_TAGGING;
static const int ASN1_F_PKCS5_PBE_SET;
-static const int ASN1_F_SMIME_READ_ASN1;
-static const int ASN1_F_SMIME_TEXT;
static const int ASN1_F_X509_CINF_NEW;
static const int ASN1_R_BOOLEAN_IS_WRONG_LENGTH;
static const int ASN1_R_BUFFER_TOO_SMALL;
@@ -86,10 +83,7 @@ static const int ASN1_R_ERROR_GETTING_TIME;
static const int ASN1_R_ERROR_LOADING_SECTION;
static const int ASN1_R_MSTRING_WRONG_TAG;
static const int ASN1_R_NESTED_ASN1_STRING;
-static const int ASN1_R_NO_CONTENT_TYPE;
static const int ASN1_R_NO_MATCHING_CHOICE_TYPE;
-static const int ASN1_R_NO_MULTIPART_BODY_FAILURE;
-static const int ASN1_R_NO_MULTIPART_BOUNDARY;
static const int ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM;
static const int ASN1_R_UNKNOWN_OBJECT_TYPE;
static const int ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE;
@@ -151,7 +145,6 @@ static const int EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED;
static const int EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH;
static const int EVP_R_DECODE_ERROR;
static const int EVP_R_DIFFERENT_KEY_TYPES;
-static const int EVP_R_DISABLED_FOR_FIPS;
static const int EVP_R_ENCODE_ERROR;
static const int EVP_R_INITIALIZATION_ERROR;
static const int EVP_R_INPUT_NOT_INITIALIZED;
@@ -258,6 +251,16 @@ int ERR_FATAL_ERROR(unsigned long);
* supporting 0.9.8
*/
void ERR_remove_thread_state(const CRYPTO_THREADID *);
+
+/* These were added in OpenSSL 0.9.8h. When we drop support for RHEL/CentOS 5
+ we should be able to move these back to TYPES. */
+static const int ASN1_F_B64_READ_ASN1;
+static const int ASN1_F_B64_WRITE_ASN1;
+static const int ASN1_F_SMIME_READ_ASN1;
+static const int ASN1_F_SMIME_TEXT;
+static const int ASN1_R_NO_CONTENT_TYPE;
+static const int ASN1_R_NO_MULTIPART_BODY_FAILURE;
+static const int ASN1_R_NO_MULTIPART_BOUNDARY;
"""
CUSTOMIZATIONS = """
@@ -266,7 +269,21 @@ static const long Cryptography_HAS_REMOVE_THREAD_STATE = 1;
#else
static const long Cryptography_HAS_REMOVE_THREAD_STATE = 0;
typedef uint32_t CRYPTO_THREADID;
-void (*ERR_remove_thread_state)(const CRYPTO_THREADID *);
+void (*ERR_remove_thread_state)(const CRYPTO_THREADID *) = NULL;
+#endif
+
+// OpenSSL 0.9.8h+
+#if OPENSSL_VERSION_NUMBER >= 0x0090808fL
+static const long Cryptography_HAS_098H_ERROR_CODES = 1;
+#else
+static const long Cryptography_HAS_098H_ERROR_CODES = 0;
+static const int ASN1_F_B64_READ_ASN1 = 0;
+static const int ASN1_F_B64_WRITE_ASN1 = 0;
+static const int ASN1_F_SMIME_READ_ASN1 = 0;
+static const int ASN1_F_SMIME_TEXT = 0;
+static const int ASN1_R_NO_CONTENT_TYPE = 0;
+static const int ASN1_R_NO_MULTIPART_BODY_FAILURE = 0;
+static const int ASN1_R_NO_MULTIPART_BOUNDARY = 0;
#endif
"""
@@ -274,4 +291,13 @@ CONDITIONAL_NAMES = {
"Cryptography_HAS_REMOVE_THREAD_STATE": [
"ERR_remove_thread_state"
],
+ "Cryptography_HAS_098H_ERROR_CODES": [
+ "ASN1_F_B64_READ_ASN1",
+ "ASN1_F_B64_WRITE_ASN1",
+ "ASN1_F_SMIME_READ_ASN1",
+ "ASN1_F_SMIME_TEXT",
+ "ASN1_R_NO_CONTENT_TYPE",
+ "ASN1_R_NO_MULTIPART_BODY_FAILURE",
+ "ASN1_R_NO_MULTIPART_BOUNDARY",
+ ],
}
diff --git a/cryptography/hazmat/bindings/openssl/evp.py b/cryptography/hazmat/bindings/openssl/evp.py
index 02776490..77128c47 100644
--- a/cryptography/hazmat/bindings/openssl/evp.py
+++ b/cryptography/hazmat/bindings/openssl/evp.py
@@ -64,8 +64,6 @@ int EVP_CipherUpdate(EVP_CIPHER_CTX *, unsigned char *, int *,
const unsigned char *, int);
int EVP_CipherFinal_ex(EVP_CIPHER_CTX *, unsigned char *, int *);
int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *);
-const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *);
-int EVP_CIPHER_block_size(const EVP_CIPHER *);
void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *);
EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *);
@@ -79,13 +77,12 @@ int EVP_DigestFinal_ex(EVP_MD_CTX *, unsigned char *, unsigned int *);
int EVP_MD_CTX_cleanup(EVP_MD_CTX *);
void EVP_MD_CTX_destroy(EVP_MD_CTX *);
const EVP_MD *EVP_get_digestbyname(const char *);
-const EVP_MD *EVP_MD_CTX_md(const EVP_MD_CTX *);
-int EVP_MD_size(const EVP_MD *);
EVP_PKEY *EVP_PKEY_new(void);
void EVP_PKEY_free(EVP_PKEY *);
int EVP_PKEY_type(int);
int EVP_PKEY_bits(EVP_PKEY *);
+int EVP_PKEY_size(EVP_PKEY *);
RSA *EVP_PKEY_get1_RSA(EVP_PKEY *);
int EVP_SignInit(EVP_MD_CTX *, const EVP_MD *);
@@ -104,6 +101,19 @@ int PKCS5_PBKDF2_HMAC_SHA1(const char *, int, const unsigned char *, int, int,
int EVP_PKEY_set1_RSA(EVP_PKEY *, struct rsa_st *);
int EVP_PKEY_set1_DSA(EVP_PKEY *, struct dsa_st *);
+
+int EVP_PKEY_get_attr_count(const EVP_PKEY *);
+int EVP_PKEY_get_attr_by_NID(const EVP_PKEY *, int, int);
+int EVP_PKEY_get_attr_by_OBJ(const EVP_PKEY *, ASN1_OBJECT *, int);
+X509_ATTRIBUTE *EVP_PKEY_get_attr(const EVP_PKEY *, int);
+X509_ATTRIBUTE *EVP_PKEY_delete_attr(EVP_PKEY *, int);
+int EVP_PKEY_add1_attr(EVP_PKEY *, X509_ATTRIBUTE *);
+int EVP_PKEY_add1_attr_by_OBJ(EVP_PKEY *, const ASN1_OBJECT *, int,
+ const unsigned char *, int);
+int EVP_PKEY_add1_attr_by_NID(EVP_PKEY *, int, int,
+ const unsigned char *, int);
+int EVP_PKEY_add1_attr_by_txt(EVP_PKEY *, const char *, int,
+ const unsigned char *, int);
"""
MACROS = """
@@ -129,6 +139,13 @@ int EVP_PKEY_sign(EVP_PKEY_CTX *, unsigned char *, size_t *,
int EVP_PKEY_verify_init(EVP_PKEY_CTX *);
int EVP_PKEY_verify(EVP_PKEY_CTX *, const unsigned char *, size_t,
const unsigned char *, size_t);
+
+/* The following were macros in 0.9.8e. Once we drop support for RHEL/CentOS 5
+ we should move these back to FUNCTIONS. */
+const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *);
+int EVP_CIPHER_block_size(const EVP_CIPHER *);
+const EVP_MD *EVP_MD_CTX_md(const EVP_MD_CTX *);
+int EVP_MD_size(const EVP_MD *);
"""
CUSTOMIZATIONS = """
@@ -140,7 +157,7 @@ const long EVP_CTRL_GCM_GET_TAG = -1;
const long EVP_CTRL_GCM_SET_TAG = -1;
const long EVP_CTRL_GCM_SET_IVLEN = -1;
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x10000000
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
const long Cryptography_HAS_PBKDF2_HMAC = 1;
const long Cryptography_HAS_PKEY_CTX = 1;
#else
diff --git a/cryptography/hazmat/bindings/openssl/hmac.py b/cryptography/hazmat/bindings/openssl/hmac.py
index 5f9e0945..4b81c9df 100644
--- a/cryptography/hazmat/bindings/openssl/hmac.py
+++ b/cryptography/hazmat/bindings/openssl/hmac.py
@@ -55,11 +55,11 @@ int Cryptography_HMAC_Update(HMAC_CTX *ctx, const unsigned char *data,
}
int Cryptography_HMAC_Final(HMAC_CTX *ctx, unsigned char *digest,
- unsigned int *digest_len) {
+ unsigned int *outlen) {
#if OPENSSL_VERSION_NUMBER >= 0x010000000
- return HMAC_Final(ctx, digest, digest_len);
+ return HMAC_Final(ctx, digest, outlen);
#else
- HMAC_Final(ctx, digest, digest_len);
+ HMAC_Final(ctx, digest, outlen);
return 1;
#endif
}
diff --git a/cryptography/hazmat/bindings/openssl/nid.py b/cryptography/hazmat/bindings/openssl/nid.py
index 40aed19f..cb83c1ba 100644
--- a/cryptography/hazmat/bindings/openssl/nid.py
+++ b/cryptography/hazmat/bindings/openssl/nid.py
@@ -14,6 +14,8 @@
INCLUDES = ""
TYPES = """
+static const int Cryptography_HAS_ECDSA_SHA2_NIDS;
+
static const int NID_undef;
static const int NID_dsa;
static const int NID_dsaWithSHA;
@@ -38,6 +40,148 @@ static const int NID_ecdsa_with_SHA512;
static const int NID_crl_reason;
static const int NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
static const int NID_subject_alt_name;
+static const int NID_X9_62_c2pnb163v1;
+static const int NID_X9_62_c2pnb163v2;
+static const int NID_X9_62_c2pnb163v3;
+static const int NID_X9_62_c2pnb176v1;
+static const int NID_X9_62_c2tnb191v1;
+static const int NID_X9_62_c2tnb191v2;
+static const int NID_X9_62_c2tnb191v3;
+static const int NID_X9_62_c2onb191v4;
+static const int NID_X9_62_c2onb191v5;
+static const int NID_X9_62_c2pnb208w1;
+static const int NID_X9_62_c2tnb239v1;
+static const int NID_X9_62_c2tnb239v2;
+static const int NID_X9_62_c2tnb239v3;
+static const int NID_X9_62_c2onb239v4;
+static const int NID_X9_62_c2onb239v5;
+static const int NID_X9_62_c2pnb272w1;
+static const int NID_X9_62_c2pnb304w1;
+static const int NID_X9_62_c2tnb359v1;
+static const int NID_X9_62_c2pnb368w1;
+static const int NID_X9_62_c2tnb431r1;
+static const int NID_X9_62_prime192v1;
+static const int NID_X9_62_prime192v2;
+static const int NID_X9_62_prime192v3;
+static const int NID_X9_62_prime239v1;
+static const int NID_X9_62_prime239v2;
+static const int NID_X9_62_prime239v3;
+static const int NID_X9_62_prime256v1;
+static const int NID_secp112r1;
+static const int NID_secp112r2;
+static const int NID_secp128r1;
+static const int NID_secp128r2;
+static const int NID_secp160k1;
+static const int NID_secp160r1;
+static const int NID_secp160r2;
+static const int NID_sect163k1;
+static const int NID_sect163r1;
+static const int NID_sect163r2;
+static const int NID_secp192k1;
+static const int NID_secp224k1;
+static const int NID_secp224r1;
+static const int NID_secp256k1;
+static const int NID_secp384r1;
+static const int NID_secp521r1;
+static const int NID_sect113r1;
+static const int NID_sect113r2;
+static const int NID_sect131r1;
+static const int NID_sect131r2;
+static const int NID_sect193r1;
+static const int NID_sect193r2;
+static const int NID_sect233k1;
+static const int NID_sect233r1;
+static const int NID_sect239k1;
+static const int NID_sect283k1;
+static const int NID_sect283r1;
+static const int NID_sect409k1;
+static const int NID_sect409r1;
+static const int NID_sect571k1;
+static const int NID_sect571r1;
+static const int NID_wap_wsg_idm_ecid_wtls1;
+static const int NID_wap_wsg_idm_ecid_wtls3;
+static const int NID_wap_wsg_idm_ecid_wtls4;
+static const int NID_wap_wsg_idm_ecid_wtls5;
+static const int NID_wap_wsg_idm_ecid_wtls6;
+static const int NID_wap_wsg_idm_ecid_wtls7;
+static const int NID_wap_wsg_idm_ecid_wtls8;
+static const int NID_wap_wsg_idm_ecid_wtls9;
+static const int NID_wap_wsg_idm_ecid_wtls10;
+static const int NID_wap_wsg_idm_ecid_wtls11;
+static const int NID_wap_wsg_idm_ecid_wtls12;
+static const int NID_ipsec3;
+static const int NID_ipsec4;
+static const char *const SN_X9_62_c2pnb163v1;
+static const char *const SN_X9_62_c2pnb163v2;
+static const char *const SN_X9_62_c2pnb163v3;
+static const char *const SN_X9_62_c2pnb176v1;
+static const char *const SN_X9_62_c2tnb191v1;
+static const char *const SN_X9_62_c2tnb191v2;
+static const char *const SN_X9_62_c2tnb191v3;
+static const char *const SN_X9_62_c2onb191v4;
+static const char *const SN_X9_62_c2onb191v5;
+static const char *const SN_X9_62_c2pnb208w1;
+static const char *const SN_X9_62_c2tnb239v1;
+static const char *const SN_X9_62_c2tnb239v2;
+static const char *const SN_X9_62_c2tnb239v3;
+static const char *const SN_X9_62_c2onb239v4;
+static const char *const SN_X9_62_c2onb239v5;
+static const char *const SN_X9_62_c2pnb272w1;
+static const char *const SN_X9_62_c2pnb304w1;
+static const char *const SN_X9_62_c2tnb359v1;
+static const char *const SN_X9_62_c2pnb368w1;
+static const char *const SN_X9_62_c2tnb431r1;
+static const char *const SN_X9_62_prime192v1;
+static const char *const SN_X9_62_prime192v2;
+static const char *const SN_X9_62_prime192v3;
+static const char *const SN_X9_62_prime239v1;
+static const char *const SN_X9_62_prime239v2;
+static const char *const SN_X9_62_prime239v3;
+static const char *const SN_X9_62_prime256v1;
+static const char *const SN_secp112r1;
+static const char *const SN_secp112r2;
+static const char *const SN_secp128r1;
+static const char *const SN_secp128r2;
+static const char *const SN_secp160k1;
+static const char *const SN_secp160r1;
+static const char *const SN_secp160r2;
+static const char *const SN_sect163k1;
+static const char *const SN_sect163r1;
+static const char *const SN_sect163r2;
+static const char *const SN_secp192k1;
+static const char *const SN_secp224k1;
+static const char *const SN_secp224r1;
+static const char *const SN_secp256k1;
+static const char *const SN_secp384r1;
+static const char *const SN_secp521r1;
+static const char *const SN_sect113r1;
+static const char *const SN_sect113r2;
+static const char *const SN_sect131r1;
+static const char *const SN_sect131r2;
+static const char *const SN_sect193r1;
+static const char *const SN_sect193r2;
+static const char *const SN_sect233k1;
+static const char *const SN_sect233r1;
+static const char *const SN_sect239k1;
+static const char *const SN_sect283k1;
+static const char *const SN_sect283r1;
+static const char *const SN_sect409k1;
+static const char *const SN_sect409r1;
+static const char *const SN_sect571k1;
+static const char *const SN_sect571r1;
+static const char *const SN_wap_wsg_idm_ecid_wtls1;
+static const char *const SN_wap_wsg_idm_ecid_wtls3;
+static const char *const SN_wap_wsg_idm_ecid_wtls4;
+static const char *const SN_wap_wsg_idm_ecid_wtls5;
+static const char *const SN_wap_wsg_idm_ecid_wtls6;
+static const char *const SN_wap_wsg_idm_ecid_wtls7;
+static const char *const SN_wap_wsg_idm_ecid_wtls8;
+static const char *const SN_wap_wsg_idm_ecid_wtls9;
+static const char *const SN_wap_wsg_idm_ecid_wtls10;
+static const char *const SN_wap_wsg_idm_ecid_wtls11;
+static const char *const SN_wap_wsg_idm_ecid_wtls12;
+static const char *const SN_ipsec3;
+static const char *const SN_ipsec4;
"""
FUNCTIONS = """
@@ -47,6 +191,23 @@ MACROS = """
"""
CUSTOMIZATIONS = """
+// OpenSSL 0.9.8g+
+#if OPENSSL_VERSION_NUMBER >= 0x0090807fL
+static const long Cryptography_HAS_ECDSA_SHA2_NIDS = 1;
+#else
+static const long Cryptography_HAS_ECDSA_SHA2_NIDS = 0;
+static const int NID_ecdsa_with_SHA224 = 0;
+static const int NID_ecdsa_with_SHA256 = 0;
+static const int NID_ecdsa_with_SHA384 = 0;
+static const int NID_ecdsa_with_SHA512 = 0;
+#endif
"""
-CONDITIONAL_NAMES = {}
+CONDITIONAL_NAMES = {
+ "Cryptography_HAS_ECDSA_SHA2_NIDS": [
+ "NID_ecdsa_with_SHA224",
+ "NID_ecdsa_with_SHA256",
+ "NID_ecdsa_with_SHA384",
+ "NID_ecdsa_with_SHA512",
+ ],
+}
diff --git a/cryptography/hazmat/bindings/openssl/osrandom_engine.py b/cryptography/hazmat/bindings/openssl/osrandom_engine.py
index 23f2e17a..0903a4bf 100644
--- a/cryptography/hazmat/bindings/openssl/osrandom_engine.py
+++ b/cryptography/hazmat/bindings/openssl/osrandom_engine.py
@@ -174,8 +174,19 @@ static RAND_METHOD osrandom_rand = {
osrandom_rand_status,
};
+/* Returns 1 if successfully added, 2 if engine has previously been added,
+ and 0 for error. */
int Cryptography_add_osrandom_engine(void) {
- ENGINE *e = ENGINE_new();
+ ENGINE *e;
+ e = ENGINE_by_id(Cryptography_osrandom_engine_id);
+ if (e != NULL) {
+ ENGINE_free(e);
+ return 2;
+ } else {
+ ERR_clear_error();
+ }
+
+ e = ENGINE_new();
if (e == NULL) {
return 0;
}
diff --git a/cryptography/hazmat/bindings/openssl/pem.py b/cryptography/hazmat/bindings/openssl/pem.py
index 8b717c2d..942cba34 100644
--- a/cryptography/hazmat/bindings/openssl/pem.py
+++ b/cryptography/hazmat/bindings/openssl/pem.py
@@ -31,9 +31,13 @@ EVP_PKEY *PEM_read_bio_PrivateKey(BIO *, EVP_PKEY **, pem_password_cb *,
int PEM_write_bio_PKCS8PrivateKey(BIO *, EVP_PKEY *, const EVP_CIPHER *,
char *, int, pem_password_cb *, void *);
+int PEM_write_bio_PKCS8PrivateKey_nid(BIO *, EVP_PKEY *, int, char *, int,
+ pem_password_cb *, void *);
int i2d_PKCS8PrivateKey_bio(BIO *, EVP_PKEY *, const EVP_CIPHER *,
char *, int, pem_password_cb *, void *);
+int i2d_PKCS8PrivateKey_nid_bio(BIO *, EVP_PKEY *, int,
+ char *, int, pem_password_cb *, void *);
EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *, EVP_PKEY **, pem_password_cb *,
void *);
diff --git a/cryptography/hazmat/bindings/openssl/rsa.py b/cryptography/hazmat/bindings/openssl/rsa.py
index b6f7d04c..f895cd02 100644
--- a/cryptography/hazmat/bindings/openssl/rsa.py
+++ b/cryptography/hazmat/bindings/openssl/rsa.py
@@ -33,7 +33,11 @@ static const int RSA_SSLV23_PADDING;
static const int RSA_NO_PADDING;
static const int RSA_PKCS1_OAEP_PADDING;
static const int RSA_X931_PADDING;
+static const int RSA_PKCS1_PSS_PADDING;
static const int RSA_F4;
+
+static const int Cryptography_HAS_PSS_PADDING;
+static const int Cryptography_HAS_MGF1_MD;
"""
FUNCTIONS = """
@@ -67,13 +71,24 @@ int RSA_padding_check_PKCS1_OAEP(unsigned char *, int, const unsigned char *,
MACROS = """
int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *, int);
int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *, int);
+int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *, EVP_MD *);
"""
CUSTOMIZATIONS = """
-#if OPENSSL_VERSION_NUMBER < 0x10000000
+#if OPENSSL_VERSION_NUMBER >= 0x10000000
+static const long Cryptography_HAS_PSS_PADDING = 1;
+#else
// see evp.py for the definition of Cryptography_HAS_PKEY_CTX
+static const long Cryptography_HAS_PSS_PADDING = 0;
int (*EVP_PKEY_CTX_set_rsa_padding)(EVP_PKEY_CTX *, int) = NULL;
int (*EVP_PKEY_CTX_set_rsa_pss_saltlen)(EVP_PKEY_CTX *, int) = NULL;
+static const long RSA_PKCS1_PSS_PADDING = 0;
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x1000100f
+static const long Cryptography_HAS_MGF1_MD = 1;
+#else
+static const long Cryptography_HAS_MGF1_MD = 0;
+int (*EVP_PKEY_CTX_set_rsa_mgf1_md)(EVP_PKEY_CTX *, EVP_MD *) = NULL;
#endif
"""
@@ -81,5 +96,11 @@ CONDITIONAL_NAMES = {
"Cryptography_HAS_PKEY_CTX": [
"EVP_PKEY_CTX_set_rsa_padding",
"EVP_PKEY_CTX_set_rsa_pss_saltlen",
- ]
+ ],
+ "Cryptography_HAS_PSS_PADDING": [
+ "RSA_PKCS1_PSS_PADDING",
+ ],
+ "Cryptography_HAS_MGF1_MD": [
+ "EVP_PKEY_CTX_set_rsa_mgf1_md",
+ ],
}
diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py
index 038ea54b..25bef49a 100644
--- a/cryptography/hazmat/bindings/openssl/ssl.py
+++ b/cryptography/hazmat/bindings/openssl/ssl.py
@@ -37,6 +37,8 @@ static const int Cryptography_HAS_RELEASE_BUFFERS;
static const int Cryptography_HAS_OP_NO_COMPRESSION;
static const int Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING;
+static const int Cryptography_HAS_SSL_SET_SSL_CTX;
+static const int Cryptography_HAS_SSL_OP_NO_TICKET;
static const int SSL_FILETYPE_PEM;
static const int SSL_FILETYPE_ASN1;
@@ -136,10 +138,13 @@ typedef struct {
typedef struct {
SSL3_STATE *s3;
SSL_SESSION *session;
+ int type;
...;
} SSL;
static const int TLSEXT_NAMETYPE_host_name;
+
+typedef ... SSL_CIPHER;
"""
FUNCTIONS = """
@@ -147,7 +152,6 @@ void SSL_load_error_strings(void);
int SSL_library_init(void);
/* SSL */
-SSL_CTX *SSL_set_SSL_CTX(SSL *, SSL_CTX *);
SSL_SESSION *SSL_get1_session(SSL *);
int SSL_set_session(SSL *, SSL_SESSION *);
int SSL_get_verify_mode(const SSL *);
@@ -184,8 +188,6 @@ int SSL_CTX_set_default_verify_paths(SSL_CTX *);
void SSL_CTX_set_verify(SSL_CTX *, int, int (*)(int, X509_STORE_CTX *));
void SSL_CTX_set_verify_depth(SSL_CTX *, int);
int (*SSL_CTX_get_verify_callback(const SSL_CTX *))(int, X509_STORE_CTX *);
-void SSL_CTX_set_info_callback(SSL_CTX *, void (*)(const SSL *, int, int));
-void (*SSL_CTX_get_info_callback(SSL_CTX *))(const SSL *, int, int);
int SSL_CTX_get_verify_mode(const SSL_CTX *);
int SSL_CTX_get_verify_depth(const SSL_CTX *);
int SSL_CTX_set_cipher_list(SSL_CTX *, const char *);
@@ -212,6 +214,14 @@ X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *);
/* SSL_SESSION */
void SSL_SESSION_free(SSL_SESSION *);
+
+/* Information about actually used cipher */
+const char *SSL_CIPHER_get_name(const SSL_CIPHER *);
+int SSL_CIPHER_get_bits(const SSL_CIPHER *, int *);
+char *SSL_CIPHER_get_version(const SSL_CIPHER *);
+
+size_t SSL_get_finished(const SSL *, void *, size_t);
+size_t SSL_get_peer_finished(const SSL *, void *, size_t);
"""
MACROS = """
@@ -281,6 +291,8 @@ const SSL_METHOD *SSLv23_client_method(void);
SSL_CTX *SSL_CTX_new(SSL_METHOD *);
long SSL_CTX_get_timeout(const SSL_CTX *);
+const SSL_CIPHER *SSL_get_current_cipher(const SSL *);
+
/* SNI APIs were introduced in OpenSSL 1.0.0. To continue to support
* earlier versions some special handling of these is necessary.
*/
@@ -289,6 +301,16 @@ void SSL_set_tlsext_host_name(SSL *, char *);
void SSL_CTX_set_tlsext_servername_callback(
SSL_CTX *,
int (*)(const SSL *, int *, void *));
+
+long SSL_session_reused(SSL *);
+
+/* The following were macros in 0.9.8e. Once we drop support for RHEL/CentOS 5
+ we should move these back to FUNCTIONS. */
+void SSL_CTX_set_info_callback(SSL_CTX *, void (*)(const SSL *, int, int));
+void (*SSL_CTX_get_info_callback(SSL_CTX *))(const SSL *, int, int);
+/* This function does not exist in 0.9.8e. Once we drop support for
+ RHEL/CentOS 5 this can be moved back to FUNCTIONS. */
+SSL_CTX *SSL_set_SSL_CTX(SSL *, SSL_CTX *);
"""
CUSTOMIZATIONS = """
@@ -356,6 +378,22 @@ const long SSL_OP_MSIE_SSLV2_RSA_PADDING = 0;
#ifdef OPENSSL_NO_EC
long (*SSL_CTX_set_tmp_ecdh)(SSL_CTX *, EC_KEY *) = NULL;
#endif
+
+#ifdef SSL_OP_NO_TICKET
+static const long Cryptography_HAS_SSL_OP_NO_TICKET = 1;
+#else
+static const long Cryptography_HAS_SSL_OP_NO_TICKET = 0;
+const long SSL_OP_NO_TICKET = 0;
+#endif
+
+// OpenSSL 0.9.8f+
+#if OPENSSL_VERSION_NUMBER >= 0x00908070L
+static const long Cryptography_HAS_SSL_SET_SSL_CTX = 1;
+#else
+static const long Cryptography_HAS_SSL_SET_SSL_CTX = 0;
+static const int TLSEXT_NAMETYPE_host_name = 0;
+SSL_CTX *(*SSL_set_SSL_CTX)(SSL *, SSL_CTX *) = NULL;
+#endif
"""
CONDITIONAL_NAMES = {
@@ -399,5 +437,14 @@ CONDITIONAL_NAMES = {
"Cryptography_HAS_EC": [
"SSL_CTX_set_tmp_ecdh",
- ]
+ ],
+
+ "Cryptography_HAS_SSL_OP_NO_TICKET": [
+ "SSL_OP_NO_TICKET",
+ ],
+
+ "Cryptography_HAS_SSL_SET_SSL_CTX": [
+ "SSL_set_SSL_CTX",
+ "TLSEXT_NAMETYPE_host_name",
+ ],
}
diff --git a/cryptography/hazmat/bindings/openssl/x509.py b/cryptography/hazmat/bindings/openssl/x509.py
index 74259b3d..e8b036c3 100644
--- a/cryptography/hazmat/bindings/openssl/x509.py
+++ b/cryptography/hazmat/bindings/openssl/x509.py
@@ -34,6 +34,8 @@ typedef struct {
...;
} X509_ALGOR;
+typedef ... X509_ATTRIBUTE;
+
typedef struct {
X509_ALGOR *signature;
...;
@@ -118,8 +120,6 @@ int X509_REQ_set_pubkey(X509_REQ *, EVP_PKEY *);
int X509_REQ_sign(X509_REQ *, EVP_PKEY *, const EVP_MD *);
int X509_REQ_verify(X509_REQ *, EVP_PKEY *);
EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *);
-int X509_REQ_add_extensions(X509_REQ *, X509_EXTENSIONS *);
-X509_EXTENSIONS *X509_REQ_get_extensions(X509_REQ *);
int X509_REQ_print_ex(BIO *, X509_REQ *, unsigned long, unsigned long);
int X509V3_EXT_print(BIO *, X509_EXTENSION *, unsigned long, int);
@@ -169,6 +169,13 @@ int X509_STORE_add_cert(X509_STORE *, X509 *);
int X509_verify_cert(X509_STORE_CTX *);
const char *X509_verify_cert_error_string(long);
+
+const char *X509_get_default_cert_area(void);
+const char *X509_get_default_cert_dir(void);
+const char *X509_get_default_cert_file(void);
+const char *X509_get_default_cert_dir_env(void);
+const char *X509_get_default_cert_file_env(void);
+const char *X509_get_default_private_dir(void);
"""
MACROS = """
@@ -199,9 +206,18 @@ X509_REVOKED *sk_X509_REVOKED_value(Cryptography_STACK_OF_X509_REVOKED *, int);
/* These aren't macros these arguments are all const X on openssl > 1.0.x */
int X509_CRL_set_lastUpdate(X509_CRL *, ASN1_TIME *);
int X509_CRL_set_nextUpdate(X509_CRL *, ASN1_TIME *);
+
+/* these use STACK_OF(X509_EXTENSION) in 0.9.8e. Once we drop support for
+ RHEL/CentOS 5 we should move these back to FUNCTIONS. */
+int X509_REQ_add_extensions(X509_REQ *, X509_EXTENSIONS *);
+X509_EXTENSIONS *X509_REQ_get_extensions(X509_REQ *);
"""
CUSTOMIZATIONS = """
+// OpenSSL 0.9.8e does not have this definition
+#if OPENSSL_VERSION_NUMBER <= 0x0090805fL
+typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS;
+#endif
"""
CONDITIONAL_NAMES = {}
diff --git a/cryptography/hazmat/primitives/asymmetric/padding.py b/cryptography/hazmat/primitives/asymmetric/padding.py
new file mode 100644
index 00000000..6bafe314
--- /dev/null
+++ b/cryptography/hazmat/primitives/asymmetric/padding.py
@@ -0,0 +1,22 @@
+# 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
+
+from cryptography import utils
+from cryptography.hazmat.primitives import interfaces
+
+
+@utils.register_interface(interfaces.AsymmetricPadding)
+class PKCS1v15(object):
+ name = "EMSA-PKCS1-v1_5"
diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py
index 01218592..dfb43340 100644
--- a/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -13,21 +13,12 @@
from __future__ import absolute_import, division, print_function
-import sys
-
import six
from cryptography import utils
from cryptography.hazmat.primitives import interfaces
-def _bit_length(x):
- if sys.version_info >= (2, 7):
- return x.bit_length()
- else:
- return len(bin(x)) - (2 + (x <= 0))
-
-
@utils.register_interface(interfaces.RSAPublicKey)
class RSAPublicKey(object):
def __init__(self, public_exponent, modulus):
@@ -49,9 +40,13 @@ class RSAPublicKey(object):
self._public_exponent = public_exponent
self._modulus = modulus
+ def verifier(self, signature, padding, algorithm, backend):
+ return backend.create_rsa_verification_ctx(self, signature, padding,
+ algorithm)
+
@property
def key_size(self):
- return _bit_length(self.modulus)
+ return utils.bit_length(self.modulus)
@property
def public_exponent(self):
@@ -132,12 +127,15 @@ class RSAPrivateKey(object):
self._modulus = modulus
@classmethod
- def generate(self, public_exponent, key_size, backend):
+ def generate(cls, public_exponent, key_size, backend):
return backend.generate_rsa_private_key(public_exponent, key_size)
+ def signer(self, padding, algorithm, backend):
+ return backend.create_rsa_signature_ctx(self, padding, algorithm)
+
@property
def key_size(self):
- return _bit_length(self.modulus)
+ return utils.bit_length(self.modulus)
def public_key(self):
return RSAPublicKey(self.public_exponent, self.modulus)
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
index 5ef469d0..3824bcde 100644
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -287,6 +287,135 @@ class RSAPublicKey(six.with_metaclass(abc.ABCMeta)):
"""
+class DSAParameters(six.with_metaclass(abc.ABCMeta)):
+ @abc.abstractproperty
+ def modulus(self):
+ """
+ The prime modulus that's used in generating the DSA keypair and used
+ in the DSA signing and verification processes.
+ """
+
+ @abc.abstractproperty
+ def subgroup_order(self):
+ """
+ The subgroup order that's used in generating the DSA keypair
+ by the generator and used in the DSA signing and verification
+ processes.
+ """
+
+ @abc.abstractproperty
+ def generator(self):
+ """
+ The generator that is used in generating the DSA keypair and used
+ in the DSA signing and verification processes.
+ """
+
+ @abc.abstractproperty
+ def p(self):
+ """
+ The prime modulus that's used in generating the DSA keypair and used
+ in the DSA signing and verification processes. Alias for modulus.
+ """
+
+ @abc.abstractproperty
+ def q(self):
+ """
+ The subgroup order that's used in generating the DSA keypair
+ by the generator and used in the DSA signing and verification
+ processes. Alias for subgroup_order.
+ """
+
+ @abc.abstractproperty
+ def g(self):
+ """
+ The generator that is used in generating the DSA keypair and used
+ in the DSA signing and verification processes. Alias for generator.
+ """
+
+
+class DSAPrivateKey(six.with_metaclass(abc.ABCMeta)):
+ @abc.abstractproperty
+ def key_size(self):
+ """
+ The bit length of the prime modulus.
+ """
+
+ @abc.abstractmethod
+ def public_key(self):
+ """
+ The DSAPublicKey associated with this private key.
+ """
+
+ @abc.abstractproperty
+ def x(self):
+ """
+ The private key "x" in the DSA structure.
+ """
+
+ @abc.abstractproperty
+ def y(self):
+ """
+ The public key.
+ """
+
+ @abc.abstractmethod
+ def parameters(self):
+ """
+ The DSAParameters object associated with this private key.
+ """
+
+
+class DSAPublicKey(six.with_metaclass(abc.ABCMeta)):
+ @abc.abstractproperty
+ def y(self):
+ """
+ The public key.
+ """
+
+ @abc.abstractmethod
+ def parameters(self):
+ """
+ The DSAParameters object associated with this public key.
+ """
+
+
+class AsymmetricSignatureContext(six.with_metaclass(abc.ABCMeta)):
+ @abc.abstractmethod
+ def update(self, data):
+ """
+ Processes the provided bytes and returns nothing.
+ """
+
+ @abc.abstractmethod
+ def finalize(self):
+ """
+ Returns the signature as bytes.
+ """
+
+
+class AsymmetricVerificationContext(six.with_metaclass(abc.ABCMeta)):
+ @abc.abstractmethod
+ def update(self, data):
+ """
+ Processes the provided bytes and returns nothing.
+ """
+
+ @abc.abstractmethod
+ def verify(self):
+ """
+ Raises an exception if the bytes provided to update do not match the
+ signature or the signature does not match the public key.
+ """
+
+
+class AsymmetricPadding(six.with_metaclass(abc.ABCMeta)):
+ @abc.abstractproperty
+ def name(self):
+ """
+ A string naming this padding (e.g. "PSS", "PKCS1").
+ """
+
+
class KeyDerivationFunction(six.with_metaclass(abc.ABCMeta)):
@abc.abstractmethod
def derive(self, key_material):
diff --git a/cryptography/hazmat/primitives/kdf/pbkdf2.py b/cryptography/hazmat/primitives/kdf/pbkdf2.py
index 71b88211..39427780 100644
--- a/cryptography/hazmat/primitives/kdf/pbkdf2.py
+++ b/cryptography/hazmat/primitives/kdf/pbkdf2.py
@@ -17,7 +17,7 @@ import six
from cryptography import utils
from cryptography.exceptions import (
- InvalidKey, UnsupportedAlgorithm, AlreadyFinalized
+ InvalidKey, UnsupportedHash, AlreadyFinalized
)
from cryptography.hazmat.primitives import constant_time, interfaces
@@ -26,7 +26,7 @@ from cryptography.hazmat.primitives import constant_time, interfaces
class PBKDF2HMAC(object):
def __init__(self, algorithm, length, salt, iterations, backend):
if not backend.pbkdf2_hmac_supported(algorithm):
- raise UnsupportedAlgorithm(
+ raise UnsupportedHash(
"{0} is not supported for PBKDF2 by this backend".format(
algorithm.name)
)
diff --git a/cryptography/hazmat/primitives/twofactor/__init__.py b/cryptography/hazmat/primitives/twofactor/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/cryptography/hazmat/primitives/twofactor/__init__.py
diff --git a/cryptography/hazmat/primitives/twofactor/hotp.py b/cryptography/hazmat/primitives/twofactor/hotp.py
new file mode 100644
index 00000000..83260225
--- /dev/null
+++ b/cryptography/hazmat/primitives/twofactor/hotp.py
@@ -0,0 +1,62 @@
+# 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 struct
+
+import six
+
+from cryptography.exceptions import InvalidToken
+from cryptography.hazmat.primitives import constant_time, hmac
+from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512
+
+
+class HOTP(object):
+ def __init__(self, key, length, algorithm, backend):
+ if len(key) < 16:
+ 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")
+
+ 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")
+
+ self._key = key
+ self._length = length
+ self._algorithm = algorithm
+ self._backend = backend
+
+ def generate(self, counter):
+ truncated_value = self._dynamic_truncate(counter)
+ hotp = truncated_value % (10 ** self._length)
+ return "{0:0{1}}".format(hotp, self._length).encode()
+
+ def verify(self, hotp, counter):
+ if not constant_time.bytes_eq(self.generate(counter), hotp):
+ 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)
+ 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
new file mode 100644
index 00000000..0630de69
--- /dev/null
+++ b/cryptography/hazmat/primitives/twofactor/totp.py
@@ -0,0 +1,32 @@
+# 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
+
+from cryptography.exceptions import InvalidToken
+from cryptography.hazmat.primitives import constant_time
+from cryptography.hazmat.primitives.twofactor.hotp import HOTP
+
+
+class TOTP(object):
+ def __init__(self, key, length, algorithm, time_step, backend):
+ self._time_step = time_step
+ self._hotp = HOTP(key, length, algorithm, backend)
+
+ def generate(self, time):
+ counter = int(time / self._time_step)
+ return self._hotp.generate(counter)
+
+ def verify(self, totp, time):
+ if not constant_time.bytes_eq(self.generate(time), totp):
+ raise InvalidToken("Supplied TOTP value does not match")