diff options
Diffstat (limited to 'cryptography/hazmat')
-rw-r--r-- | cryptography/hazmat/backends/interfaces.py | 10 | ||||
-rw-r--r-- | cryptography/hazmat/backends/multibackend.py | 20 | ||||
-rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 191 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/commoncrypto/binding.py | 6 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/bignum.py | 13 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/binding.py | 10 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/dsa.py | 13 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/ec.py | 2 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/err.py | 3 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/evp.py | 11 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/ssl.py | 28 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/x509.py | 54 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/utils.py | 5 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/asymmetric/padding.py | 13 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/asymmetric/rsa.py | 45 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/cmac.py | 75 |
16 files changed, 469 insertions, 30 deletions
diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py index 92413d8c..1ddf0785 100644 --- a/cryptography/hazmat/backends/interfaces.py +++ b/cryptography/hazmat/backends/interfaces.py @@ -117,6 +117,12 @@ class RSABackend(object): Return True if the hash algorithm is supported for MGF1 in PSS. """ + @abc.abstractmethod + def decrypt_rsa(self, private_key, ciphertext, padding): + """ + Returns decrypted bytes. + """ + @six.add_metaclass(abc.ABCMeta) class DSABackend(object): @@ -135,9 +141,9 @@ class DSABackend(object): @six.add_metaclass(abc.ABCMeta) -class OpenSSLSerializationBackend(object): +class TraditionalOpenSSLSerializationBackend(object): @abc.abstractmethod - def load_openssl_pem_private_key(self, data, password): + def load_traditional_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 86cded85..981a60bd 100644 --- a/cryptography/hazmat/backends/multibackend.py +++ b/cryptography/hazmat/backends/multibackend.py @@ -16,11 +16,12 @@ from __future__ import absolute_import, division, print_function from cryptography import utils from cryptography.exceptions import UnsupportedAlgorithm, _Reasons from cryptography.hazmat.backends.interfaces import ( - CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, - RSABackend + CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend, + PBKDF2HMACBackend, RSABackend ) +@utils.register_interface(CMACBackend) @utils.register_interface(CipherBackend) @utils.register_interface(HashBackend) @utils.register_interface(HMACBackend) @@ -156,3 +157,18 @@ class MultiBackend(object): return b.generate_dsa_private_key(parameters) raise UnsupportedAlgorithm("DSA is not supported by the backend", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + + def cmac_algorithm_supported(self, algorithm): + return any( + b.cmac_algorithm_supported(algorithm) + for b in self._filtered_backends(CMACBackend) + ) + + def create_cmac_ctx(self, algorithm): + for b in self._filtered_backends(CMACBackend): + try: + return b.create_cmac_ctx(algorithm) + except UnsupportedAlgorithm: + pass + 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 9ac062c2..2d7e2307 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -25,14 +25,14 @@ from cryptography.exceptions import ( UnsupportedAlgorithm, _Reasons ) from cryptography.hazmat.backends.interfaces import ( - CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, - RSABackend + CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend, + PBKDF2HMACBackend, RSABackend ) from cryptography.hazmat.bindings.openssl.binding import Binding from cryptography.hazmat.primitives import hashes, interfaces from cryptography.hazmat.primitives.asymmetric import dsa, rsa from cryptography.hazmat.primitives.asymmetric.padding import ( - MGF1, PKCS1v15, PSS + MGF1, OAEP, PKCS1v15, PSS ) from cryptography.hazmat.primitives.ciphers.algorithms import ( AES, ARC4, Blowfish, CAST5, Camellia, IDEA, SEED, TripleDES @@ -47,6 +47,7 @@ _OpenSSLError = collections.namedtuple("_OpenSSLError", @utils.register_interface(CipherBackend) +@utils.register_interface(CMACBackend) @utils.register_interface(DSABackend) @utils.register_interface(HashBackend) @utils.register_interface(HMACBackend) @@ -293,7 +294,7 @@ class Backend(object): self._lib.OPENSSL_free(hex_cdata) return int(hex_str, 16) - def _int_to_bn(self, num): + def _int_to_bn(self, num, bn=None): """ Converts a python integer to a BIGNUM. The returned BIGNUM will not be garbage collected (to support adding them to structs that take @@ -301,11 +302,14 @@ class Backend(object): be discarded after use. """ + if bn is None: + bn = self._ffi.NULL + if six.PY3: # Python 3 has constant time to_bytes, so use that. binary = num.to_bytes(int(num.bit_length() / 8.0 + 1), "big") - bn_ptr = self._lib.BN_bin2bn(binary, len(binary), self._ffi.NULL) + bn_ptr = self._lib.BN_bin2bn(binary, len(binary), bn) assert bn_ptr != self._ffi.NULL return bn_ptr @@ -314,6 +318,7 @@ class Backend(object): hex_num = hex(num).rstrip("L").lstrip("0x").encode("ascii") or b"0" bn_ptr = self._ffi.new("BIGNUM **") + bn_ptr[0] = bn res = self._lib.BN_hex2bn(bn_ptr, hex_num) assert res != 0 assert bn_ptr[0] != self._ffi.NULL @@ -346,7 +351,7 @@ class Backend(object): def _new_evp_pkey(self): evp_pkey = self._lib.EVP_PKEY_new() assert evp_pkey != self._ffi.NULL - return self._ffi.gc(evp_pkey, backend._lib.EVP_PKEY_free) + return self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) def _rsa_private_key_to_evp_pkey(self, private_key): evp_pkey = self._new_evp_pkey() @@ -469,6 +474,120 @@ class Backend(object): y=self._bn_to_int(ctx.pub_key) ) + def decrypt_rsa(self, private_key, ciphertext, padding): + if isinstance(padding, PKCS1v15): + padding_enum = self._lib.RSA_PKCS1_PADDING + elif isinstance(padding, OAEP): + padding_enum = self._lib.RSA_PKCS1_OAEP_PADDING + if not isinstance(padding._mgf, MGF1): + raise UnsupportedAlgorithm( + "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", + _Reasons.UNSUPPORTED_HASH + ) + + if padding._label is not None and padding._label != b"": + 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", + _Reasons.UNSUPPORTED_HASH + ) + else: + raise UnsupportedAlgorithm( + "{0} is not supported by this backend".format( + padding.name + ), + _Reasons.UNSUPPORTED_PADDING + ) + + key_size_bytes = int(math.ceil(private_key.key_size / 8.0)) + if key_size_bytes < len(ciphertext): + raise ValueError("Ciphertext too large for key size") + + if self._lib.Cryptography_HAS_PKEY_CTX: + return self._decrypt_rsa_pkey_ctx(private_key, ciphertext, + padding_enum) + else: + return self._decrypt_rsa_098(private_key, ciphertext, padding_enum) + + def _decrypt_rsa_pkey_ctx(self, private_key, ciphertext, padding_enum): + evp_pkey = self._rsa_private_key_to_evp_pkey(private_key) + pkey_ctx = self._lib.EVP_PKEY_CTX_new( + evp_pkey, self._ffi.NULL + ) + assert pkey_ctx != self._ffi.NULL + pkey_ctx = self._ffi.gc(pkey_ctx, self._lib.EVP_PKEY_CTX_free) + res = self._lib.EVP_PKEY_decrypt_init(pkey_ctx) + assert res == 1 + res = self._lib.EVP_PKEY_CTX_set_rsa_padding( + pkey_ctx, padding_enum) + assert res > 0 + buf_size = self._lib.EVP_PKEY_size(evp_pkey) + assert buf_size > 0 + outlen = self._ffi.new("size_t *", buf_size) + buf = self._ffi.new("char[]", buf_size) + res = self._lib.Cryptography_EVP_PKEY_decrypt( + pkey_ctx, + buf, + outlen, + ciphertext, + len(ciphertext) + ) + if res <= 0: + errors = self._consume_errors() + assert errors + assert errors[0].lib == self._lib.ERR_LIB_RSA + 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") + + return self._ffi.buffer(buf)[:outlen[0]] + + def _decrypt_rsa_098(self, private_key, ciphertext, padding_enum): + rsa_cdata = self._rsa_cdata_from_private_key(private_key) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + key_size = self._lib.RSA_size(rsa_cdata) + assert key_size > 0 + buf = self._ffi.new("unsigned char[]", key_size) + res = self._lib.RSA_private_decrypt( + len(ciphertext), + ciphertext, + buf, + rsa_cdata, + padding_enum + ) + if res < 0: + errors = self._consume_errors() + assert errors + assert errors[0].lib == self._lib.ERR_LIB_RSA + 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") + + return self._ffi.buffer(buf)[:res] + + def cmac_algorithm_supported(self, algorithm): + return ( + self._lib.Cryptography_HAS_CMAC == 1 + and self.cipher_supported(algorithm, CBC( + b"\x00" * algorithm.block_size)) + ) + + def create_cmac_ctx(self, algorithm): + return _CMACContext(self, algorithm) + class GetCipherByName(object): def __init__(self, fmt): @@ -1155,4 +1274,64 @@ class _RSAVerificationContext(object): raise InvalidSignature +@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", + _Reasons.UNSUPPORTED_CIPHER) + + self._backend = backend + self._key = algorithm.key + self._algorithm = algorithm + self._output_length = algorithm.block_size // 8 + + if ctx is None: + registry = self._backend._cipher_registry + adapter = registry[type(algorithm), CBC] + + evp_cipher = adapter(self._backend, algorithm, CBC) + + ctx = self._backend._lib.CMAC_CTX_new() + + assert ctx != self._backend._ffi.NULL + ctx = self._backend._ffi.gc(ctx, self._backend._lib.CMAC_CTX_free) + + self._backend._lib.CMAC_Init( + ctx, self._key, len(self._key), + evp_cipher, self._backend._ffi.NULL + ) + + self._ctx = ctx + + def update(self, data): + res = self._backend._lib.CMAC_Update(self._ctx, data, len(data)) + assert res == 1 + + def finalize(self): + buf = self._backend._ffi.new("unsigned char[]", self._output_length) + length = self._backend._ffi.new("size_t *", self._output_length) + res = self._backend._lib.CMAC_Final( + self._ctx, buf, length + ) + assert res == 1 + + self._ctx = None + + return self._backend._ffi.buffer(buf)[:] + + def copy(self): + copied_ctx = self._backend._lib.CMAC_CTX_new() + copied_ctx = self._backend._ffi.gc( + copied_ctx, self._backend._lib.CMAC_CTX_free + ) + res = self._backend._lib.CMAC_CTX_copy( + copied_ctx, self._ctx + ) + assert res == 1 + return _CMACContext( + self._backend, self._algorithm, ctx=copied_ctx + ) + + backend = Backend() diff --git a/cryptography/hazmat/bindings/commoncrypto/binding.py b/cryptography/hazmat/bindings/commoncrypto/binding.py index 3673ea36..144bb099 100644 --- a/cryptography/hazmat/bindings/commoncrypto/binding.py +++ b/cryptography/hazmat/bindings/commoncrypto/binding.py @@ -42,8 +42,10 @@ class Binding(object): if cls.ffi is not None and cls.lib is not None: return - cls.ffi, cls.lib = build_ffi(cls._module_prefix, cls._modules, - "", "", []) + cls.ffi, cls.lib = build_ffi( + module_prefix=cls._module_prefix, + modules=cls._modules, + ) @classmethod def is_available(cls): diff --git a/cryptography/hazmat/bindings/openssl/bignum.py b/cryptography/hazmat/bindings/openssl/bignum.py index 94c2914e..1d944ee9 100644 --- a/cryptography/hazmat/bindings/openssl/bignum.py +++ b/cryptography/hazmat/bindings/openssl/bignum.py @@ -87,12 +87,25 @@ int BN_mod_exp(BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); int BN_gcd(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); BIGNUM *BN_mod_inverse(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); + +int BN_set_bit(BIGNUM *, int); +int BN_clear_bit(BIGNUM *, int); + +int BN_is_bit_set(const BIGNUM *, int); + +int BN_mask_bits(BIGNUM *, int); """ MACROS = """ int BN_zero(BIGNUM *); int BN_one(BIGNUM *); int BN_mod(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); + +int BN_lshift(BIGNUM *, const BIGNUM *, int); +int BN_lshift1(BIGNUM *, BIGNUM *); + +int BN_rshift(BIGNUM *, BIGNUM *, int); +int BN_rshift1(BIGNUM *, BIGNUM *); """ CUSTOMIZATIONS = """ diff --git a/cryptography/hazmat/bindings/openssl/binding.py b/cryptography/hazmat/bindings/openssl/binding.py index cc40a108..f0ff3275 100644 --- a/cryptography/hazmat/bindings/openssl/binding.py +++ b/cryptography/hazmat/bindings/openssl/binding.py @@ -97,9 +97,13 @@ class Binding(object): else: # pragma: no cover libraries = ["libeay32", "ssleay32", "advapi32"] - cls.ffi, cls.lib = build_ffi(cls._module_prefix, cls._modules, - _OSX_PRE_INCLUDE, _OSX_POST_INCLUDE, - libraries) + cls.ffi, cls.lib = build_ffi( + module_prefix=cls._module_prefix, + modules=cls._modules, + pre_include=_OSX_PRE_INCLUDE, + post_include=_OSX_POST_INCLUDE, + libraries=libraries, + ) res = cls.lib.Cryptography_add_osrandom_engine() assert res != 0 diff --git a/cryptography/hazmat/bindings/openssl/dsa.py b/cryptography/hazmat/bindings/openssl/dsa.py index 40d3b8ee..7db03326 100644 --- a/cryptography/hazmat/bindings/openssl/dsa.py +++ b/cryptography/hazmat/bindings/openssl/dsa.py @@ -31,6 +31,10 @@ typedef struct dsa_st { BIGNUM *pub_key; ...; } DSA; +typedef struct { + BIGNUM *r; + BIGNUM *s; +} DSA_SIG; """ FUNCTIONS = """ @@ -39,6 +43,15 @@ DSA *DSA_generate_parameters(int, unsigned char *, int, int *, unsigned long *, int DSA_generate_key(DSA *); DSA *DSA_new(void); void DSA_free(DSA *); +DSA_SIG *DSA_SIG_new(void); +void DSA_SIG_free(DSA_SIG *); +int i2d_DSA_SIG(const DSA_SIG *, unsigned char **); +DSA_SIG *d2i_DSA_SIG(DSA_SIG **, const unsigned char **, long); +int DSA_size(const DSA *); +int DSA_sign(int, const unsigned char *, int, unsigned char *, unsigned int *, + DSA *); +int DSA_verify(int, const unsigned char *, int, const unsigned char *, int, + DSA *); """ MACROS = """ diff --git a/cryptography/hazmat/bindings/openssl/ec.py b/cryptography/hazmat/bindings/openssl/ec.py index 8ae2ae9a..45c17c2e 100644 --- a/cryptography/hazmat/bindings/openssl/ec.py +++ b/cryptography/hazmat/bindings/openssl/ec.py @@ -77,6 +77,7 @@ EC_KEY *EC_KEY_copy(EC_KEY *, const EC_KEY *); EC_KEY *EC_KEY_dup(const EC_KEY *); int EC_KEY_up_ref(EC_KEY *); const EC_GROUP *EC_KEY_get0_group(const EC_KEY *); +int EC_GROUP_get_order(const EC_GROUP *, BIGNUM *, BN_CTX *); int EC_KEY_set_group(EC_KEY *, const EC_GROUP *); const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *); int EC_KEY_set_private_key(EC_KEY *, const BIGNUM *); @@ -214,6 +215,7 @@ EC_KEY *(*EC_KEY_copy)(EC_KEY *, const EC_KEY *) = NULL; EC_KEY *(*EC_KEY_dup)(const EC_KEY *) = NULL; int (*EC_KEY_up_ref)(EC_KEY *) = NULL; const EC_GROUP *(*EC_KEY_get0_group)(const EC_KEY *) = NULL; +int (*EC_GROUP_get_order)(const EC_GROUP *, BIGNUM *, BN_CTX *) = NULL; int (*EC_KEY_set_group)(EC_KEY *, const EC_GROUP *) = NULL; const BIGNUM *(*EC_KEY_get0_private_key)(const EC_KEY *) = NULL; int (*EC_KEY_set_private_key)(EC_KEY *, const BIGNUM *) = NULL; diff --git a/cryptography/hazmat/bindings/openssl/err.py b/cryptography/hazmat/bindings/openssl/err.py index f51393aa..f6456d66 100644 --- a/cryptography/hazmat/bindings/openssl/err.py +++ b/cryptography/hazmat/bindings/openssl/err.py @@ -138,6 +138,7 @@ 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; +static const int EVP_R_BAD_DECRYPT; static const int EVP_R_BAD_KEY_LENGTH; static const int EVP_R_BN_DECODE_ERROR; static const int EVP_R_BN_PUBKEY_ERROR; @@ -216,6 +217,8 @@ static const int PEM_R_UNSUPPORTED_ENCRYPTION; static const int RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE; static const int RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY; +static const int RSA_R_BLOCK_TYPE_IS_NOT_01; +static const int RSA_R_BLOCK_TYPE_IS_NOT_02; """ FUNCTIONS = """ diff --git a/cryptography/hazmat/bindings/openssl/evp.py b/cryptography/hazmat/bindings/openssl/evp.py index 963537c8..b3d958e6 100644 --- a/cryptography/hazmat/bindings/openssl/evp.py +++ b/cryptography/hazmat/bindings/openssl/evp.py @@ -87,6 +87,8 @@ int EVP_PKEY_type(int); int EVP_PKEY_bits(EVP_PKEY *); int EVP_PKEY_size(EVP_PKEY *); RSA *EVP_PKEY_get1_RSA(EVP_PKEY *); +DSA *EVP_PKEY_get1_DSA(EVP_PKEY *); +DH *EVP_PKEY_get1_DH(EVP_PKEY *); int EVP_SignInit(EVP_MD_CTX *, const EVP_MD *); int EVP_SignUpdate(EVP_MD_CTX *, const void *, size_t); @@ -104,6 +106,7 @@ 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_set1_DH(EVP_PKEY *, DH *); int EVP_PKEY_get_attr_count(const EVP_PKEY *); int EVP_PKEY_get_attr_by_NID(const EVP_PKEY *, int, int); @@ -123,7 +126,11 @@ MACROS = """ void OpenSSL_add_all_algorithms(void); int EVP_PKEY_assign_RSA(EVP_PKEY *, RSA *); int EVP_PKEY_assign_DSA(EVP_PKEY *, DSA *); + int EVP_PKEY_assign_EC_KEY(EVP_PKEY *, EC_KEY *); +EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *); +int EVP_PKEY_set1_EC_KEY(EVP_PKEY *, EC_KEY *); + int EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *); int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *, int, int, void *); @@ -215,6 +222,8 @@ int (*Cryptography_EVP_PKEY_decrypt)(EVP_PKEY_CTX *, unsigned char *, size_t *, #endif #ifdef OPENSSL_NO_EC int (*EVP_PKEY_assign_EC_KEY)(EVP_PKEY *, EC_KEY *) = NULL; +EC_KEY *(*EVP_PKEY_get1_EC_KEY)(EVP_PKEY *) = NULL; +int (*EVP_PKEY_set1_EC_KEY)(EVP_PKEY *, EC_KEY *) = NULL; #endif """ @@ -245,5 +254,7 @@ CONDITIONAL_NAMES = { ], "Cryptography_HAS_EC": [ "EVP_PKEY_assign_EC_KEY", + "EVP_PKEY_get1_EC_KEY", + "EVP_PKEY_set1_EC_KEY", ] } diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py index 094310f3..7ed42f9f 100644 --- a/cryptography/hazmat/bindings/openssl/ssl.py +++ b/cryptography/hazmat/bindings/openssl/ssl.py @@ -237,26 +237,28 @@ size_t SSL_get_peer_finished(const SSL *, void *, size_t); """ MACROS = """ -long SSL_set_mode(SSL *, long); -long SSL_get_mode(SSL *); +unsigned long SSL_set_mode(SSL *, unsigned long); +unsigned long SSL_get_mode(SSL *); -long SSL_set_options(SSL *, long); -long SSL_get_options(SSL *); +unsigned long SSL_set_options(SSL *, unsigned long); +unsigned long SSL_get_options(SSL *); int SSL_want_read(const SSL *); int SSL_want_write(const SSL *); long SSL_total_renegotiations(SSL *); -long SSL_CTX_set_options(SSL_CTX *, long); -long SSL_CTX_get_options(SSL_CTX *); -long SSL_CTX_set_mode(SSL_CTX *, long); -long SSL_CTX_get_mode(SSL_CTX *); -long SSL_CTX_set_session_cache_mode(SSL_CTX *, long); -long SSL_CTX_get_session_cache_mode(SSL_CTX *); -long SSL_CTX_set_tmp_dh(SSL_CTX *, DH *); -long SSL_CTX_set_tmp_ecdh(SSL_CTX *, EC_KEY *); -long SSL_CTX_add_extra_chain_cert(SSL_CTX *, X509 *); +/* Defined as unsigned long because SSL_OP_ALL is greater than signed 32-bit + and Windows defines long as 32-bit. */ +unsigned long SSL_CTX_set_options(SSL_CTX *, unsigned long); +unsigned long SSL_CTX_get_options(SSL_CTX *); +unsigned long SSL_CTX_set_mode(SSL_CTX *, unsigned long); +unsigned long SSL_CTX_get_mode(SSL_CTX *); +unsigned long SSL_CTX_set_session_cache_mode(SSL_CTX *, unsigned long); +unsigned long SSL_CTX_get_session_cache_mode(SSL_CTX *); +unsigned long SSL_CTX_set_tmp_dh(SSL_CTX *, DH *); +unsigned long SSL_CTX_set_tmp_ecdh(SSL_CTX *, EC_KEY *); +unsigned long SSL_CTX_add_extra_chain_cert(SSL_CTX *, X509 *); /*- These aren't macros these functions are all const X on openssl > 1.0.x -*/ diff --git a/cryptography/hazmat/bindings/openssl/x509.py b/cryptography/hazmat/bindings/openssl/x509.py index e800d272..36a15e4a 100644 --- a/cryptography/hazmat/bindings/openssl/x509.py +++ b/cryptography/hazmat/bindings/openssl/x509.py @@ -160,6 +160,8 @@ X509_REQ *d2i_X509_REQ_bio(BIO *, X509_REQ **); int i2d_PrivateKey_bio(BIO *, EVP_PKEY *); EVP_PKEY *d2i_PrivateKey_bio(BIO *, EVP_PKEY **); +int i2d_PUBKEY_bio(BIO *, EVP_PKEY *); +EVP_PKEY *d2i_PUBKEY_bio(BIO *, EVP_PKEY **); ASN1_INTEGER *X509_get_serialNumber(X509 *); int X509_set_serialNumber(X509 *, ASN1_INTEGER *); @@ -178,6 +180,27 @@ 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); + +int i2d_RSA_PUBKEY(RSA *, unsigned char **); +RSA *d2i_RSA_PUBKEY(RSA **, const unsigned char **, long); +RSA *d2i_RSAPublicKey(RSA **, const unsigned char **, long); +RSA *d2i_RSAPrivateKey(RSA **, const unsigned char **, long); +int i2d_DSA_PUBKEY(DSA *, unsigned char **); +DSA *d2i_DSA_PUBKEY(DSA **, const unsigned char **, long); +DSA *d2i_DSAPublicKey(DSA **, const unsigned char **, long); +DSA *d2i_DSAPrivateKey(DSA **, const unsigned char **, long); + + +RSA *d2i_RSAPrivateKey_bio(BIO *, RSA **); +int i2d_RSAPrivateKey_bio(BIO *, RSA *); +RSA *d2i_RSAPublicKey_bio(BIO *, RSA **); +int i2d_RSAPublicKey_bio(BIO *, RSA *); +RSA *d2i_RSA_PUBKEY_bio(BIO *, RSA **); +int i2d_RSA_PUBKEY_bio(BIO *, RSA *); +DSA *d2i_DSA_PUBKEY_bio(BIO *, DSA **); +int i2d_DSA_PUBKEY_bio(BIO *, DSA *); +DSA *d2i_DSAPrivateKey_bio(BIO *, DSA **); +int i2d_DSAPrivateKey_bio(BIO *, DSA *); """ MACROS = """ @@ -205,6 +228,11 @@ void sk_X509_EXTENSION_free(X509_EXTENSIONS *); int sk_X509_REVOKED_num(Cryptography_STACK_OF_X509_REVOKED *); X509_REVOKED *sk_X509_REVOKED_value(Cryptography_STACK_OF_X509_REVOKED *, int); +int i2d_RSAPublicKey(RSA *, unsigned char **); +int i2d_RSAPrivateKey(RSA *, unsigned char **); +int i2d_DSAPublicKey(DSA *, unsigned char **); +int i2d_DSAPrivateKey(DSA *, unsigned char **); + /* 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 *); @@ -213,6 +241,13 @@ int X509_CRL_set_nextUpdate(X509_CRL *, ASN1_TIME *); 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 *); + +int i2d_EC_PUBKEY(EC_KEY *, unsigned char **); +EC_KEY *d2i_EC_PUBKEY(EC_KEY **, const unsigned char **, long); +EC_KEY *d2i_EC_PUBKEY_bio(BIO *, EC_KEY **); +int i2d_EC_PUBKEY_bio(BIO *, EC_KEY *); +EC_KEY *d2i_ECPrivateKey_bio(BIO *, EC_KEY **); +int i2d_ECPrivateKey_bio(BIO *, EC_KEY *); """ CUSTOMIZATIONS = """ @@ -220,6 +255,23 @@ CUSTOMIZATIONS = """ #if OPENSSL_VERSION_NUMBER <= 0x0090805fL typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS; #endif +#ifdef OPENSSL_NO_EC +int (*i2d_EC_PUBKEY)(EC_KEY *, unsigned char **) = NULL; +EC_KEY *(*d2i_EC_PUBKEY)(EC_KEY **, const unsigned char **, long) = NULL; +EC_KEY *(*d2i_EC_PUBKEY_bio)(BIO *, EC_KEY **) = NULL; +int (*i2d_EC_PUBKEY_bio)(BIO *, EC_KEY *) = NULL; +EC_KEY *(*d2i_ECPrivateKey_bio)(BIO *, EC_KEY **) = NULL; +int (*i2d_ECPrivateKey_bio)(BIO *, EC_KEY *) = NULL; +#endif """ -CONDITIONAL_NAMES = {} +CONDITIONAL_NAMES = { + "Cryptography_HAS_EC": [ + "i2d_EC_PUBKEY", + "d2i_EC_PUBKEY", + "d2i_EC_PUBKEY_bio", + "i2d_EC_PUBKEY_bio", + "d2i_ECPrivateKey_bio", + "i2d_ECPrivateKey_bio", + ] +} diff --git a/cryptography/hazmat/bindings/utils.py b/cryptography/hazmat/bindings/utils.py index 318b82bb..1c48116e 100644 --- a/cryptography/hazmat/bindings/utils.py +++ b/cryptography/hazmat/bindings/utils.py @@ -20,7 +20,8 @@ import sys import cffi -def build_ffi(module_prefix, modules, pre_include, post_include, libraries): +def build_ffi(module_prefix, modules, pre_include="", post_include="", + libraries=[], extra_compile_args=[], extra_link_args=[]): """ Modules listed in ``modules`` should have the following attributes: @@ -75,6 +76,8 @@ def build_ffi(module_prefix, modules, pre_include, post_include, libraries): modulename=_create_modulename(cdef_sources, source, sys.version), libraries=libraries, ext_package="cryptography", + extra_compile_args=extra_compile_args, + extra_link_args=extra_link_args, ) for name in modules: diff --git a/cryptography/hazmat/primitives/asymmetric/padding.py b/cryptography/hazmat/primitives/asymmetric/padding.py index 72806a61..dcc6fe06 100644 --- a/cryptography/hazmat/primitives/asymmetric/padding.py +++ b/cryptography/hazmat/primitives/asymmetric/padding.py @@ -54,6 +54,19 @@ class PSS(object): self._salt_length = salt_length +@utils.register_interface(interfaces.AsymmetricPadding) +class OAEP(object): + name = "EME-OAEP" + + def __init__(self, mgf, algorithm, label): + if not isinstance(algorithm, interfaces.HashAlgorithm): + raise TypeError("Expected instance of interfaces.HashAlgorithm.") + + self._mgf = mgf + self._algorithm = algorithm + self._label = label + + class MGF1(object): MAX_LENGTH = object() diff --git a/cryptography/hazmat/primitives/asymmetric/rsa.py b/cryptography/hazmat/primitives/asymmetric/rsa.py index 94cc4645..cffd4e98 100644 --- a/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -73,6 +73,42 @@ class RSAPublicKey(object): return self.modulus +def _modinv(e, m): + """ + Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1 + """ + x1, y1, x2, y2 = 1, 0, 0, 1 + a, b = e, m + while b > 0: + q, r = divmod(a, b) + xn, yn = x1 - q * x2, y1 - q * y2 + a, b, x1, y1, x2, y2 = b, r, x2, y2, xn, yn + return x1 % m + + +def rsa_crt_iqmp(p, q): + """ + Compute the CRT (q ** -1) % p value from RSA primes p and q. + """ + return _modinv(q, p) + + +def rsa_crt_dmp1(private_exponent, p): + """ + Compute the CRT private_exponent % (p - 1) value from the RSA + private_exponent and p. + """ + return private_exponent % (p - 1) + + +def rsa_crt_dmq1(private_exponent, q): + """ + Compute the CRT private_exponent % (q - 1) value from the RSA + private_exponent and q. + """ + return private_exponent % (q - 1) + + @utils.register_interface(interfaces.RSAPrivateKey) class RSAPrivateKey(object): def __init__(self, p, q, private_exponent, dmp1, dmq1, iqmp, @@ -153,6 +189,15 @@ class RSAPrivateKey(object): return backend.create_rsa_signature_ctx(self, padding, algorithm) + def decrypt(self, ciphertext, padding, backend): + if not isinstance(backend, RSABackend): + raise UnsupportedAlgorithm( + "Backend object does not implement RSABackend", + _Reasons.BACKEND_MISSING_INTERFACE + ) + + return backend.decrypt_rsa(self, ciphertext, padding) + @property def key_size(self): return utils.bit_length(self.modulus) diff --git a/cryptography/hazmat/primitives/cmac.py b/cryptography/hazmat/primitives/cmac.py new file mode 100644 index 00000000..7e7f65ab --- /dev/null +++ b/cryptography/hazmat/primitives/cmac.py @@ -0,0 +1,75 @@ +# 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 import utils +from cryptography.exceptions import ( + AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import CMACBackend +from cryptography.hazmat.primitives import constant_time, interfaces + + +@utils.register_interface(interfaces.CMACContext) +class CMAC(object): + def __init__(self, algorithm, backend, ctx=None): + if not isinstance(backend, CMACBackend): + raise UnsupportedAlgorithm( + "Backend object does not implement CMACBackend", + _Reasons.BACKEND_MISSING_INTERFACE + ) + + if not isinstance(algorithm, interfaces.BlockCipherAlgorithm): + raise TypeError( + "Expected instance of interfaces.BlockCipherAlgorithm" + ) + self._algorithm = algorithm + + self._backend = backend + if ctx is None: + self._ctx = self._backend.create_cmac_ctx(self._algorithm) + else: + self._ctx = ctx + + 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") + self._ctx.update(data) + + def finalize(self): + if self._ctx is None: + 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") + 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") + return CMAC( + self._algorithm, + backend=self._backend, + ctx=self._ctx.copy() + ) |