diff options
48 files changed, 1938 insertions, 143 deletions
diff --git a/.travis/install.sh b/.travis/install.sh index 58d7404d..79790050 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -32,12 +32,12 @@ if [[ "$(uname -s)" == "Darwin" ]]; then if which pyenv > /dev/null; then eval "$(pyenv init -)"; fi case "${TOX_ENV}" in py26) - curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py + curl -O https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py sudo python get-pip.py sudo pip install virtualenv ;; py27) - curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py + curl -O https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py sudo python get-pip.py sudo pip install virtualenv ;; @@ -62,7 +62,7 @@ if [[ "$(uname -s)" == "Darwin" ]]; then pip install virtualenv ;; docs) - curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py + curl -O https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py sudo python get-pip.py sudo pip install virtualenv ;; diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9e89e563..2fdf8c9b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,8 @@ Changelog :class:`~cryptography.hazmat.primitives.asymmetric.padding.MGF1` and added it to :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`. It will be removed from ``MGF1`` in two releases per our :doc:`/api-stability` policy. +* Added :class:`~cryptography.hazmat.primitives.ciphers.algorithms.SEED` support. +* Added :class:`~cryptography.hazmat.primitives.cmac.CMAC`. 0.3 - 2014-03-27 ~~~~~~~~~~~~~~~~ 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() + ) diff --git a/dev-requirements.txt b/dev-requirements.txt index 9dabba1b..092b9914 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,5 +1,6 @@ coverage flake8 +flake8-import-order invoke iso8601 pep8-naming @@ -7,8 +8,8 @@ pretend pytest requests sphinx -sphinxcontrib-spelling sphinx_rtd_theme +sphinxcontrib-spelling tox twine -e . diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst index 394d060b..11ff9305 100644 --- a/docs/hazmat/backends/interfaces.rst +++ b/docs/hazmat/backends/interfaces.rst @@ -9,7 +9,7 @@ Backend interfaces Backend implementations may provide a number of interfaces to support operations such as :doc:`/hazmat/primitives/symmetric-encryption`, :doc:`/hazmat/primitives/cryptographic-hashes`, and -:doc:`/hazmat/primitives/hmac`. +:doc:`/hazmat/primitives/mac/hmac`. A specific ``backend`` may provide one or more of these interfaces. @@ -263,8 +263,20 @@ A specific ``backend`` may provide one or more of these interfaces. :returns: ``True`` if the specified ``algorithm`` is supported by this backend, otherwise ``False``. + .. method:: decrypt_rsa(private_key, ciphertext, padding) -.. class:: OpenSSLSerializationBackend + :param private_key: An instance of an + :class:`~cryptography.hazmat.primitives.interfaces.RSAPrivateKey` + provider. + + :param bytes ciphertext: The ciphertext to decrypt. + + :param padding: An instance of an + :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding` + provider. + + +.. class:: TraditionalOpenSSLSerializationBackend .. versionadded:: 0.3 @@ -278,8 +290,8 @@ A specific ``backend`` may provide one or more of these interfaces. :param bytes password: The password to use if this data is encrypted. Should be None if the data is not encrypted. - :return: A new instance of - :class:`~cryptography.hazmat.primitives.serialization.OpenSSLPrivateKey` + :return: A new instance of the appropriate private key or public key + that the serialized data contains. :raises ValueError: If the data could not be deserialized correctly. @@ -295,12 +307,12 @@ A specific ``backend`` may provide one or more of these interfaces. .. method:: generate_dsa_parameters(key_size) - :param int key_size: The length of the modulus in bits. It should be - either "1024, 2048 or 3072". For keys generated in 2014 this should + :param int key_size: The length of the modulus in bits. It should be + either 1024, 2048 or 3072. For keys generated in 2014 this should be at least 2048. - Note that some applications (such as SSH) have not yet gained support - for larger key sizes specified in FIPS 186-3 and are still restricted - to only the 1024-bit keys specified in FIPS 186-2. + Note that some applications (such as SSH) have not yet gained + support for larger key sizes specified in FIPS 186-3 and are still + restricted to only the 1024-bit keys specified in FIPS 186-2. :return: A new instance of a :class:`~cryptography.hazmat.primitives.interfaces.DSAParameters` @@ -316,9 +328,10 @@ A specific ``backend`` may provide one or more of these interfaces. :class:`~cryptography.hazmat.primitives.interfaces.DSAPrivateKey` provider. - :raises ValueError: This is raised if the key size is not (1024 or 2048 or 3072) - or if the OpenSSL version is older than 1.0.0 and the key size is larger than 1024 - because older OpenSSL versions don't support a key size larger than 1024. + :raises ValueError: This is raised if the key size is not one of 1024, + 2048, or 3072. It is also raised when OpenSSL is older than version + 1.0.0 and the key size is larger than 1024; older OpenSSL versions + do not support keys larger than 1024 bits. .. class:: CMACBackend diff --git a/docs/hazmat/backends/openssl.rst b/docs/hazmat/backends/openssl.rst index fdfadf0b..43e5d8f3 100644 --- a/docs/hazmat/backends/openssl.rst +++ b/docs/hazmat/backends/openssl.rst @@ -73,6 +73,6 @@ from the same pool as ``/dev/random``. .. _`OpenSSL`: https://www.openssl.org/ -.. _`initializing the RNG`: http://en.wikipedia.org/wiki/OpenSSL#Vulnerability_in_the_Debian_implementation +.. _`initializing the RNG`: https://en.wikipedia.org/wiki/OpenSSL#Predictable_keys_.28Debian-specific.29 .. _`Yarrow`: http://en.wikipedia.org/wiki/Yarrow_algorithm .. _`Microsoft documentation`: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379942(v=vs.85).aspx diff --git a/docs/hazmat/primitives/asymmetric/padding.rst b/docs/hazmat/primitives/asymmetric/padding.rst index 89af7eaa..40084799 100644 --- a/docs/hazmat/primitives/asymmetric/padding.rst +++ b/docs/hazmat/primitives/asymmetric/padding.rst @@ -19,7 +19,8 @@ Padding PSS (Probabilistic Signature Scheme) is a signature scheme defined in :rfc:`3447`. It is more complex than PKCS1 but possesses a `security proof`_. - This is the `recommended padding algorithm`_ for RSA signatures. + This is the `recommended padding algorithm`_ for RSA signatures. It cannot + be used with RSA encryption. :param mgf: A mask generation function object. At this time the only supported MGF is :class:`MGF1`. @@ -32,12 +33,28 @@ Padding Pass this attribute to ``salt_length`` to get the maximum salt length available. +.. class:: OAEP(mgf, label) + + .. versionadded:: 0.4 + + OAEP (Optimal Asymmetric Encryption Padding) is a padding scheme defined in + :rfc:`3447`. It provides probabilistic encryption and is `proven secure`_ + against several attack types. This is the `recommended padding algorithm`_ + for RSA encryption. It cannot be used with RSA signing. + + :param mgf: A mask generation function object. At this time the only + supported MGF is :class:`MGF1`. + + :param bytes label: A label to apply. This is a rarely used field and + should typically be set to ``None`` or ``b""``, which are equivalent. + .. class:: PKCS1v15() .. versionadded:: 0.3 PKCS1 v1.5 (also known as simply PKCS1) is a simple padding scheme - developed for use with RSA keys. It is defined in :rfc:`3447`. + developed for use with RSA keys. It is defined in :rfc:`3447`. This padding + can be used for signing and encryption. Mask generation functions ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -60,3 +77,4 @@ Mask generation functions .. _`Padding is critical`: http://rdist.root.org/2009/10/06/why-rsa-encryption-padding-is-critical/ .. _`security proof`: http://eprint.iacr.org/2001/062.pdf .. _`recommended padding algorithm`: http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html +.. _`proven secure`: http://cseweb.ucsd.edu/users/mihir/papers/oae.pdf diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst index 5074f1c5..862df635 100644 --- a/docs/hazmat/primitives/asymmetric/rsa.rst +++ b/docs/hazmat/primitives/asymmetric/rsa.rst @@ -116,6 +116,60 @@ RSA :raises ValueError: This is raised when the chosen hash algorithm is too large for the key size. + .. method:: decrypt(ciphertext, padding, backend) + + .. versionadded:: 0.4 + + Decrypt data that was encrypted with the public key. + + :param bytes ciphertext: The ciphertext to decrypt. + + :param padding: An instance of a + :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding` + provider. + + :param backend: A + :class:`~cryptography.hazmat.backends.interfaces.RSABackend` + provider. + + :return bytes: Decrypted data. + + :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if + the provided ``backend`` does not implement + :class:`~cryptography.hazmat.backends.interfaces.RSABackend` or if + the backend does not support the chosen hash or padding algorithm. + If the padding is + :class:`~cryptography.hazmat.primitives.asymmetric.padding.OAEP` + with the + :class:`~cryptography.hazmat.primitives.asymmetric.padding.MGF1` + mask generation function it may also refer to the ``MGF1`` hash + algorithm. + + :raises TypeError: This is raised when the padding is not an + :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding` + provider. + + :raises ValueError: This is raised when decryption fails or the data + is too large for the key size. If the padding is + :class:`~cryptography.hazmat.primitives.asymmetric.padding.OAEP` + it may also be raised for invalid label values. + + .. code-block:: python + + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes + from cryptography.hazmat.primitives.asymmetric import padding + + plaintext = private_key.decrypt( + ciphertext, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ), + default_backend() + ) + .. class:: RSAPublicKey(public_exponent, modulus) @@ -214,7 +268,42 @@ RSA too large for the key size. +Handling partial RSA private keys +--------------------------------- + +If you are trying to load RSA private keys yourself you may find that not all +parameters required by ``RSAPrivateKey`` are available. In particular the +`Chinese Remainder Theorem`_ (CRT) values ``dmp1``, ``dmq1``, ``iqmp`` may be +missing or present in a different form. For example `OpenPGP`_ does not include +the ``iqmp``, ``dmp1`` or ``dmq1`` parameters. + +The following functions are provided for users who want to work with keys like +this without having to do the math themselves. + +.. function:: rsa_crt_iqmp(p, q) + + .. versionadded:: 0.4 + + Generates the ``iqmp`` (also known as ``qInv``) parameter from the RSA + primes ``p`` and ``q``. + +.. function:: rsa_crt_dmp1(private_exponent, p) + + .. versionadded:: 0.4 + + Generates the ``dmp1`` parameter from the RSA private exponent and prime + ``p``. + +.. function:: rsa_crt_dmq1(private_exponent, q) + + .. versionadded:: 0.4 + + Generates the ``dmq1`` parameter from the RSA private exponent and prime + ``q``. + .. _`RSA`: https://en.wikipedia.org/wiki/RSA_(cryptosystem) .. _`public-key`: https://en.wikipedia.org/wiki/Public-key_cryptography .. _`use 65537`: http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html .. _`at least 2048`: http://www.ecrypt.eu.org/documents/D.SPA.20.pdf +.. _`OpenPGP`: https://en.wikipedia.org/wiki/Pretty_Good_Privacy +.. _`Chinese Remainder Theorem`: http://en.wikipedia.org/wiki/RSA_%28cryptosystem%29#Using_the_Chinese_remainder_algorithm diff --git a/docs/hazmat/primitives/index.rst b/docs/hazmat/primitives/index.rst index 90deec8b..a9ab38a0 100644 --- a/docs/hazmat/primitives/index.rst +++ b/docs/hazmat/primitives/index.rst @@ -7,7 +7,7 @@ Primitives :maxdepth: 1 cryptographic-hashes - hmac + mac/index symmetric-encryption padding key-derivation-functions diff --git a/docs/hazmat/primitives/interfaces.rst b/docs/hazmat/primitives/interfaces.rst index 95fd6f9f..3b837a0d 100644 --- a/docs/hazmat/primitives/interfaces.rst +++ b/docs/hazmat/primitives/interfaces.rst @@ -133,6 +133,24 @@ Asymmetric interfaces :returns: :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext` + .. method:: decrypt(ciphertext, padding, backend) + + .. versionadded:: 0.4 + + Decrypt data that was encrypted via the public key. + + :param bytes ciphertext: The ciphertext to decrypt. + + :param padding: An instance of a + :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding` + provider. + + :param backend: A + :class:`~cryptography.hazmat.backends.interfaces.RSABackend` + provider. + + :return bytes: Decrypted data. + .. method:: public_key() :return: :class:`~cryptography.hazmat.primitives.interfaces.RSAPublicKey` diff --git a/docs/hazmat/primitives/mac/cmac.rst b/docs/hazmat/primitives/mac/cmac.rst new file mode 100644 index 00000000..a6b048b5 --- /dev/null +++ b/docs/hazmat/primitives/mac/cmac.rst @@ -0,0 +1,107 @@ +.. hazmat:: + +Cipher-based message authentication code +======================================== + +.. currentmodule:: cryptography.hazmat.primitives.cmac + +.. testsetup:: + + import binascii + key = binascii.unhexlify(b"0" * 32) + +`Cipher-based message authentication codes`_ (or CMACs) are a tool for calculating +message authentication codes using a block cipher coupled with a +secret key. You can use an CMAC to verify both the integrity and authenticity +of a message. + +A subset of CMAC with the AES-128 algorithm is described in :rfc:`4493`. + +.. class:: CMAC(algorithm, backend) + + .. versionadded:: 0.4 + + CMAC objects take a + :class:`~cryptography.hazmat.primitives.interfaces.BlockCipherAlgorithm` provider. + + .. doctest:: + + >>> from cryptography.hazmat.backends import default_backend + >>> from cryptography.hazmat.primitives import cmac + >>> from cryptography.hazmat.primitives.ciphers import algorithms + >>> c = cmac.CMAC(algorithms.AES(key), backend=default_backend()) + >>> c.update(b"message to authenticate") + >>> c.finalize() + 'CT\x1d\xc8\x0e\x15\xbe4e\xdb\xb6\x84\xca\xd9Xk' + + If the backend doesn't support the requested ``algorithm`` an + :class:`~cryptography.exceptions.UnsupportedAlgorithm` exception will be + raised. + + If the `algorithm`` isn't a + :class:`~cryptography.primitives.interfaces.BlockCipherAlgorithm` provider, + ``TypeError`` will be raised. + + To check that a given signature is correct use the :meth:`verify` method. + You will receive an exception if the signature is wrong: + + .. code-block:: pycon + + >>> c.verify(b"an incorrect signature") + Traceback (most recent call last): + ... + cryptography.exceptions.InvalidSignature: Signature did not match digest. + + :param algorithm: An + :class:`~cryptography.hazmat.primitives.interfaces.BlockCipherAlgorithm` + provider. + :param backend: An + :class:`~cryptography.hazmat.backends.interfaces.CMACBackend` + provider. + :raises TypeError: This is raised if the provided ``algorithm`` is not an instance of + :class:`~cryptography.hazmat.primitives.interfaces.BlockCipherAlgorithm` + :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if the + provided ``backend`` does not implement + :class:`~cryptography.hazmat.backends.interfaces.CMACBackend` + + .. method:: update(data) + + :param bytes data: The bytes to hash and authenticate. + :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` + + .. method:: copy() + + Copy this :class:`CMAC` instance, usually so that we may call + :meth:`finalize` to get an intermediate value while we continue + to call :meth:`update` on the original instance. + + :return: A new instance of :class:`CMAC` that can be updated + and finalized independently of the original instance. + :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` + + .. method:: verify(signature) + + Finalize the current context and securely compare the MAC to + ``signature``. + + :param bytes signature: The bytes to compare the current CMAC + against. + :raises cryptography.exceptions.AlreadyFinalized: See :meth:`finalize` + :raises cryptography.exceptions.InvalidSignature: If signature does not + match digest + + .. method:: finalize() + + Finalize the current context and return the message authentication code + as bytes. + + After ``finalize`` has been called this object can no longer be used + and :meth:`update`, :meth:`copy`, :meth:`verify` and :meth:`finalize` + will raise an :class:`~cryptography.exceptions.AlreadyFinalized` + exception. + + :return bytes: The message authentication code as bytes. + :raises cryptography.exceptions.AlreadyFinalized: + + +.. _`Cipher-based message authentication codes`: https://en.wikipedia.org/wiki/CMAC diff --git a/docs/hazmat/primitives/hmac.rst b/docs/hazmat/primitives/mac/hmac.rst index 11b10735..11b10735 100644 --- a/docs/hazmat/primitives/hmac.rst +++ b/docs/hazmat/primitives/mac/hmac.rst diff --git a/docs/hazmat/primitives/mac/index.rst b/docs/hazmat/primitives/mac/index.rst new file mode 100644 index 00000000..59fb8da2 --- /dev/null +++ b/docs/hazmat/primitives/mac/index.rst @@ -0,0 +1,10 @@ +.. hazmat:: + +Message Authentication Codes +============================ + +.. toctree:: + :maxdepth: 1 + + cmac + hmac diff --git a/docs/hazmat/primitives/padding.rst b/docs/hazmat/primitives/padding.rst index d23f31bb..3056eb92 100644 --- a/docs/hazmat/primitives/padding.rst +++ b/docs/hazmat/primitives/padding.rst @@ -5,7 +5,7 @@ Padding .. currentmodule:: cryptography.hazmat.primitives.padding -Padding is a way to take data that may or may not be be a multiple of the block +Padding is a way to take data that may or may not be a multiple of the block size for a cipher and extend it out so that it is. This is required for many block cipher modes as they require the data to be encrypted to be an exact multiple of the block size. diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst index 1a4df222..c2692ae2 100644 --- a/docs/hazmat/primitives/symmetric-encryption.rst +++ b/docs/hazmat/primitives/symmetric-encryption.rst @@ -21,7 +21,7 @@ message but an attacker can create bogus messages and force the application to decrypt them. For this reason it is *strongly* recommended to combine encryption with a -message authentication code, such as :doc:`HMAC </hazmat/primitives/hmac>`, in +message authentication code, such as :doc:`HMAC </hazmat/primitives/mac/hmac>`, in an "encrypt-then-MAC" formulation as `described by Colin Percival`_. .. class:: Cipher(algorithm, mode, backend) @@ -289,7 +289,7 @@ Modes block cipher mode that simultaneously encrypts the message as well as authenticating it. Additional unencrypted data may also be authenticated. Additional means of verifying integrity such as - :doc:`HMAC </hazmat/primitives/hmac>` are not necessary. + :doc:`HMAC </hazmat/primitives/mac/hmac>` are not necessary. **This mode does not require padding.** diff --git a/docs/installation.rst b/docs/installation.rst index a0dd5f22..3ebbecfd 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -10,16 +10,27 @@ You can install ``cryptography`` with ``pip``: Supported platforms ------------------- -Currently we test ``cryptography`` on Python 2.6, 2.7, 3.2, 3.3 and PyPy on -these operating systems. +Currently we test ``cryptography`` on Python 2.6, 2.7, 3.2, 3.3, 3.4 and PyPy +on these operating systems. -* x86-64 CentOS 6.4 and CentOS 5 +* x86-64 CentOS 6.4 and CentOS 5.x * x86-64 FreeBSD 9.2 and FreeBSD 10 * OS X 10.9 Mavericks, 10.8 Mountain Lion, and 10.7 Lion * x86-64 Ubuntu 12.04 LTS * 32-bit Python on 64-bit Windows Server 2008 * 64-bit Python on 64-bit Windows Server 2012 +We test compiling with ``clang`` as well as ``gcc`` and use the following +OpenSSL releases: + +* ``OpenSSL 0.9.8e-fips-rhel5`` (``RHEL/CentOS 5``) +* ``OpenSSL 0.9.8y`` +* ``OpenSSL 1.0.0-fips`` (``RHEL/CentOS 6.4``) +* ``OpenSSL 1.0.1`` +* ``OpenSSL 1.0.1e-freebsd`` +* ``OpenSSL 1.0.1g`` +* ``OpenSSL 1.0.2 beta`` + On Windows ---------- diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index 02b9f9ef..aae52ae7 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -25,6 +25,7 @@ interoperable introspectability invariants iOS +metadata pickleable plaintext pseudorandom @@ -2,9 +2,11 @@ addopts = -r s markers = cipher: this test requires a backend providing CipherBackend + cmac: this test requires a backend providing CMACBackend dsa: this test requires a backend providing DSABackend hash: this test requires a backend providing HashBackend hmac: this test requires a backend providing HMACBackend pbkdf2hmac: this test requires a backend providing PBKDF2HMACBackend rsa: this test requires a backend providing RSABackend + traditional_openssl_serialization: this test requires a backend providing TraditionalOpenSSLSerializationBackend supported: parametrized test requiring only_if and skip_message @@ -14,9 +14,9 @@ from __future__ import absolute_import, division, print_function import os +import subprocess import sys from distutils.command.build import build -import subprocess import pkg_resources diff --git a/tests/conftest.py b/tests/conftest.py index 1ee2a993..86d5a03b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,10 +17,9 @@ import pytest from cryptography.hazmat.backends import _available_backends from cryptography.hazmat.backends.interfaces import ( - CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, - RSABackend + CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend, + PBKDF2HMACBackend, RSABackend, TraditionalOpenSSLSerializationBackend ) - from .utils import check_backend_support, check_for_iface, select_backends @@ -36,10 +35,16 @@ def pytest_generate_tests(metafunc): def pytest_runtest_setup(item): check_for_iface("hmac", HMACBackend, item) check_for_iface("cipher", CipherBackend, item) + check_for_iface("cmac", CMACBackend, item) check_for_iface("hash", HashBackend, item) check_for_iface("pbkdf2hmac", PBKDF2HMACBackend, item) check_for_iface("dsa", DSABackend, item) check_for_iface("rsa", RSABackend, item) + check_for_iface( + "traditional_openssl_serialization", + TraditionalOpenSSLSerializationBackend, + item + ) check_backend_support(item) diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py index f46009d4..d8c09bd7 100644 --- a/tests/hazmat/backends/test_multibackend.py +++ b/tests/hazmat/backends/test_multibackend.py @@ -18,11 +18,11 @@ 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.backends.multibackend import MultiBackend -from cryptography.hazmat.primitives import hashes, hmac +from cryptography.hazmat.primitives import cmac, hashes, hmac from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes @@ -108,6 +108,19 @@ class DummyDSABackend(object): pass +@utils.register_interface(CMACBackend) +class DummyCMACBackend(object): + def __init__(self, supported_algorithms): + self._algorithms = supported_algorithms + + def cmac_algorithm_supported(self, algorithm): + return type(algorithm) in self._algorithms + + def create_cmac_ctx(self, algorithm): + if not self.cmac_algorithm_supported(algorithm): + raise UnsupportedAlgorithm("", _Reasons.UNSUPPORTED_CIPHER) + + class TestMultiBackend(object): def test_ciphers(self): backend = MultiBackend([ @@ -224,3 +237,18 @@ class TestMultiBackend(object): _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM ): backend.generate_dsa_private_key(parameters) + + def test_cmac(self): + backend = MultiBackend([ + DummyCMACBackend([algorithms.AES]) + ]) + + fake_key = b"\x00" * 16 + + assert backend.cmac_algorithm_supported( + algorithms.AES(fake_key)) is True + + cmac.CMAC(algorithms.AES(fake_key), backend) + + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): + cmac.CMAC(algorithms.TripleDES(fake_key), backend) diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 4f682f66..58511666 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -23,6 +23,7 @@ from cryptography.hazmat.primitives.asymmetric import dsa, padding, rsa from cryptography.hazmat.primitives.ciphers import Cipher from cryptography.hazmat.primitives.ciphers.algorithms import AES from cryptography.hazmat.primitives.ciphers.modes import CBC +from cryptography.hazmat.primitives.interfaces import BlockCipherAlgorithm from ...utils import raises_unsupported_algorithm @@ -143,8 +144,8 @@ class TestOpenSSL(object): with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH): backend.derive_pbkdf2_hmac(hashes.SHA256(), 10, b"", 1000, b"") - # This test is not in the next class because to check if it's really - # default we don't want to run the setup_method before it + # This test is not in the TestOpenSSLRandomEngine class because to check + # if it's really default we don't want to run the setup_method before it def test_osrandom_engine_is_default(self): e = backend._lib.ENGINE_get_default_RAND() name = backend._lib.ENGINE_get_name(e) @@ -233,6 +234,25 @@ class TestOpenSSLRandomEngine(object): e = backend._lib.ENGINE_get_default_RAND() assert e == backend._ffi.NULL + def test_int_to_bn(self): + value = (2 ** 4242) - 4242 + bn = backend._int_to_bn(value) + assert bn != backend._ffi.NULL + bn = backend._ffi.gc(bn, backend._lib.BN_free) + + assert bn + assert backend._bn_to_int(bn) == value + + def test_int_to_bn_inplace(self): + value = (2 ** 4242) - 4242 + bn_ptr = backend._lib.BN_new() + assert bn_ptr != backend._ffi.NULL + bn_ptr = backend._ffi.gc(bn_ptr, backend._lib.BN_free) + bn = backend._int_to_bn(value, bn_ptr) + + assert bn == bn_ptr + assert backend._bn_to_int(bn_ptr) == value + class TestOpenSSLRSA(object): @pytest.mark.skipif( @@ -272,3 +292,69 @@ class TestOpenSSLRSA(object): def test_unsupported_mgf1_hash_algorithm(self): assert backend.mgf1_hash_supported(DummyHash()) is False + + def test_unsupported_mgf1_hash_algorithm_decrypt(self): + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH): + private_key.decrypt( + b"ciphertext", + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA1(), + label=None + ), + backend + ) + + def test_unsupported_oaep_hash_algorithm_decrypt(self): + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_HASH): + private_key.decrypt( + b"ciphertext", + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA256(), + label=None + ), + backend + ) + + def test_unsupported_oaep_label_decrypt(self): + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) + with pytest.raises(ValueError): + private_key.decrypt( + b"ciphertext", + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=b"label" + ), + backend + ) + + +@pytest.mark.skipif( + backend._lib.OPENSSL_VERSION_NUMBER <= 0x10001000, + reason="Requires an OpenSSL version >= 1.0.1" +) +class TestOpenSSLCMAC(object): + def test_unsupported_cipher(self): + @utils.register_interface(BlockCipherAlgorithm) + class FakeAlgorithm(object): + def __init__(self): + self.block_size = 64 + + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_CIPHER): + backend.create_cmac_ctx(FakeAlgorithm()) diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py index acab22b1..58d7602b 100644 --- a/tests/hazmat/bindings/test_openssl.py +++ b/tests/hazmat/bindings/test_openssl.py @@ -84,7 +84,7 @@ class TestOpenSSL(object): with pytest.raises(RuntimeError): b._lock_cb(0, b.lib.CRYPTO_LOCK_SSL, "<test>", 1) - # errors shouldnt cause locking + # errors shouldn't cause locking assert lock.acquire(False) lock.release() @@ -103,3 +103,37 @@ class TestOpenSSL(object): b = Binding() res = b.lib.Cryptography_add_osrandom_engine() assert res == 2 + + def test_ssl_ctx_options(self): + # Test that we're properly handling 32-bit unsigned on all platforms. + b = Binding() + assert b.lib.SSL_OP_ALL > 0 + ctx = b.lib.SSL_CTX_new(b.lib.TLSv1_method()) + ctx = b.ffi.gc(ctx, b.lib.SSL_CTX_free) + resp = b.lib.SSL_CTX_set_options(ctx, b.lib.SSL_OP_ALL) + assert resp == b.lib.SSL_OP_ALL + assert b.lib.SSL_OP_ALL == b.lib.SSL_CTX_get_options(ctx) + + def test_ssl_options(self): + # Test that we're properly handling 32-bit unsigned on all platforms. + b = Binding() + assert b.lib.SSL_OP_ALL > 0 + ctx = b.lib.SSL_CTX_new(b.lib.TLSv1_method()) + ctx = b.ffi.gc(ctx, b.lib.SSL_CTX_free) + ssl = b.lib.SSL_new(ctx) + ssl = b.ffi.gc(ssl, b.lib.SSL_free) + resp = b.lib.SSL_set_options(ssl, b.lib.SSL_OP_ALL) + assert resp == b.lib.SSL_OP_ALL + assert b.lib.SSL_OP_ALL == b.lib.SSL_get_options(ssl) + + def test_ssl_mode(self): + # Test that we're properly handling 32-bit unsigned on all platforms. + b = Binding() + assert b.lib.SSL_OP_ALL > 0 + ctx = b.lib.SSL_CTX_new(b.lib.TLSv1_method()) + ctx = b.ffi.gc(ctx, b.lib.SSL_CTX_free) + ssl = b.lib.SSL_new(ctx) + ssl = b.ffi.gc(ssl, b.lib.SSL_free) + resp = b.lib.SSL_set_mode(ssl, b.lib.SSL_OP_ALL) + assert resp == b.lib.SSL_OP_ALL + assert b.lib.SSL_OP_ALL == b.lib.SSL_get_mode(ssl) diff --git a/tests/hazmat/primitives/test_cmac.py b/tests/hazmat/primitives/test_cmac.py new file mode 100644 index 00000000..7ec4af68 --- /dev/null +++ b/tests/hazmat/primitives/test_cmac.py @@ -0,0 +1,217 @@ +# 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 binascii + +import pretend + +import pytest + +import six + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, InvalidSignature, _Reasons +) +from cryptography.hazmat.backends.interfaces import CMACBackend +from cryptography.hazmat.primitives.ciphers.algorithms import ( + AES, ARC4, TripleDES +) +from cryptography.hazmat.primitives.cmac import CMAC + +from tests.utils import ( + load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm +) + +vectors_aes128 = load_vectors_from_file( + "CMAC/nist-800-38b-aes128.txt", load_nist_vectors) + +vectors_aes192 = load_vectors_from_file( + "CMAC/nist-800-38b-aes192.txt", load_nist_vectors) + +vectors_aes256 = load_vectors_from_file( + "CMAC/nist-800-38b-aes256.txt", load_nist_vectors) + +vectors_aes = vectors_aes128 + vectors_aes192 + vectors_aes256 + +vectors_3des = load_vectors_from_file( + "CMAC/nist-800-38b-3des.txt", load_nist_vectors) + +fake_key = b"\x00" * 16 + + +@pytest.mark.cmac +class TestCMAC(object): + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), + skip_message="Does not support CMAC." + ) + @pytest.mark.parametrize("params", vectors_aes) + def test_aes_generate(self, backend, params): + key = params["key"] + message = params["message"] + output = params["output"] + + cmac = CMAC(AES(binascii.unhexlify(key)), backend) + cmac.update(binascii.unhexlify(message)) + assert binascii.hexlify(cmac.finalize()) == output + + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), + skip_message="Does not support CMAC." + ) + @pytest.mark.parametrize("params", vectors_aes) + def test_aes_verify(self, backend, params): + key = params["key"] + message = params["message"] + output = params["output"] + + cmac = CMAC(AES(binascii.unhexlify(key)), backend) + cmac.update(binascii.unhexlify(message)) + assert cmac.verify(binascii.unhexlify(output)) is None + + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + TripleDES(fake_key)), + skip_message="Does not support CMAC." + ) + @pytest.mark.parametrize("params", vectors_3des) + def test_3des_generate(self, backend, params): + key1 = params["key1"] + key2 = params["key2"] + key3 = params["key3"] + + key = key1 + key2 + key3 + + message = params["message"] + output = params["output"] + + cmac = CMAC(TripleDES(binascii.unhexlify(key)), backend) + cmac.update(binascii.unhexlify(message)) + assert binascii.hexlify(cmac.finalize()) == output + + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + TripleDES(fake_key)), + skip_message="Does not support CMAC." + ) + @pytest.mark.parametrize("params", vectors_3des) + def test_3des_verify(self, backend, params): + key1 = params["key1"] + key2 = params["key2"] + key3 = params["key3"] + + key = key1 + key2 + key3 + + message = params["message"] + output = params["output"] + + cmac = CMAC(TripleDES(binascii.unhexlify(key)), backend) + cmac.update(binascii.unhexlify(message)) + assert cmac.verify(binascii.unhexlify(output)) is None + + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), + skip_message="Does not support CMAC." + ) + def test_invalid_verify(self, backend): + key = b"2b7e151628aed2a6abf7158809cf4f3c" + cmac = CMAC(AES(key), backend) + cmac.update(b"6bc1bee22e409f96e93d7e117393172a") + + with pytest.raises(InvalidSignature): + cmac.verify(b"foobar") + + @pytest.mark.supported( + only_if=lambda backend: backend.cipher_supported( + ARC4(fake_key), None), + skip_message="Does not support CMAC." + ) + def test_invalid_algorithm(self, backend): + key = b"0102030405" + with pytest.raises(TypeError): + CMAC(ARC4(key), backend) + + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), + skip_message="Does not support CMAC." + ) + def test_raises_after_finalize(self, backend): + key = b"2b7e151628aed2a6abf7158809cf4f3c" + cmac = CMAC(AES(key), backend) + cmac.finalize() + + with pytest.raises(AlreadyFinalized): + cmac.update(b"foo") + + with pytest.raises(AlreadyFinalized): + cmac.copy() + + with pytest.raises(AlreadyFinalized): + cmac.finalize() + + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), + skip_message="Does not support CMAC." + ) + def test_verify_reject_unicode(self, backend): + key = b"2b7e151628aed2a6abf7158809cf4f3c" + cmac = CMAC(AES(key), backend) + + with pytest.raises(TypeError): + cmac.update(six.u('')) + + with pytest.raises(TypeError): + cmac.verify(six.u('')) + + @pytest.mark.supported( + only_if=lambda backend: backend.cmac_algorithm_supported( + AES(fake_key)), + skip_message="Does not support CMAC." + ) + def test_copy_with_backend(self, backend): + key = b"2b7e151628aed2a6abf7158809cf4f3c" + cmac = CMAC(AES(key), backend) + cmac.update(b"6bc1bee22e409f96e93d7e117393172a") + copy_cmac = cmac.copy() + assert cmac.finalize() == copy_cmac.finalize() + + +def test_copy(): + @utils.register_interface(CMACBackend) + class PretendBackend(object): + pass + + pretend_backend = PretendBackend() + copied_ctx = pretend.stub() + pretend_ctx = pretend.stub(copy=lambda: copied_ctx) + key = b"2b7e151628aed2a6abf7158809cf4f3c" + cmac = CMAC(AES(key), backend=pretend_backend, ctx=pretend_ctx) + + assert cmac._backend is pretend_backend + assert cmac.copy()._backend is pretend_backend + + +def test_invalid_backend(): + key = b"2b7e151628aed2a6abf7158809cf4f3c" + pretend_backend = object() + + with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE): + CMAC(AES(key), pretend_backend) diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py index 2b5d4bb3..bc3b1db6 100644 --- a/tests/hazmat/primitives/test_dsa.py +++ b/tests/hazmat/primitives/test_dsa.py @@ -23,7 +23,7 @@ from cryptography.hazmat.primitives.asymmetric import dsa from cryptography.utils import bit_length from ...utils import ( - load_vectors_from_file, load_fips_dsa_key_pair_vectors, + load_fips_dsa_key_pair_vectors, load_vectors_from_file, raises_unsupported_algorithm ) diff --git a/tests/hazmat/primitives/test_pbkdf2hmac.py b/tests/hazmat/primitives/test_pbkdf2hmac.py index 62ca0921..e928fc6a 100644 --- a/tests/hazmat/primitives/test_pbkdf2hmac.py +++ b/tests/hazmat/primitives/test_pbkdf2hmac.py @@ -14,6 +14,7 @@ from __future__ import absolute_import, division, print_function import pytest + import six from cryptography import utils diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index 1cbd1636..34b80cc3 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -26,7 +26,9 @@ from cryptography.exceptions import _Reasons from cryptography.hazmat.primitives import hashes, interfaces from cryptography.hazmat.primitives.asymmetric import padding, rsa -from .utils import generate_rsa_verification_test +from .utils import ( + _check_rsa_private_key, generate_rsa_verification_test +) from ...utils import ( load_pkcs1_vectors, load_rsa_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm @@ -42,37 +44,6 @@ class DummyMGF(object): _salt_length = 0 -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 _check_rsa_private_key(skey): - assert skey - assert skey.modulus - assert skey.public_exponent - assert skey.private_exponent - assert skey.p * skey.q == skey.modulus - assert skey.key_size - assert skey.dmp1 == skey.d % (skey.p - 1) - assert skey.dmq1 == skey.d % (skey.q - 1) - assert skey.iqmp == _modinv(skey.q, skey.p) - - pkey = skey.public_key() - assert pkey - assert skey.modulus == pkey.modulus - assert skey.public_exponent == pkey.public_exponent - assert skey.key_size == pkey.key_size - - def _flatten_pkcs1_examples(vectors): flattened_vectors = [] for vector in vectors: @@ -97,7 +68,7 @@ def test_modular_inverse(): "b2347cfcd669133088d1c159518531025297c2d67c9da856a12e80222cd03b4c6ec0f" "86c957cb7bb8de7a127b645ec9e820aa94581e4762e209f01", 16 ) - assert _modinv(q, p) == int( + assert rsa._modinv(q, p) == int( "0275e06afa722999315f8f322275483e15e2fb46d827b17800f99110b269a6732748f" "624a382fa2ed1ec68c99f7fc56fb60e76eea51614881f497ba7034c17dde955f92f15" "772f8b2b41f3e56d88b1e096cdd293eba4eae1e82db815e0fadea0c4ec971bc6fd875" @@ -108,7 +79,7 @@ def test_modular_inverse(): @pytest.mark.rsa class TestRSA(object): @pytest.mark.parametrize( - "public_exponent,key_size", + ("public_exponent", "key_size"), itertools.product( (3, 5, 65537), (1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1536, 2048) @@ -1238,3 +1209,157 @@ class TestMGF1(object): mgf = padding.MGF1(algorithm, padding.MGF1.MAX_LENGTH) assert mgf._algorithm == algorithm assert mgf._salt_length == padding.MGF1.MAX_LENGTH + + +class TestOAEP(object): + def test_invalid_algorithm(self): + mgf = padding.MGF1(hashes.SHA1()) + with pytest.raises(TypeError): + padding.OAEP( + mgf=mgf, + algorithm=b"", + label=None + ) + + +@pytest.mark.rsa +class TestRSADecryption(object): + @pytest.mark.parametrize( + "vector", + _flatten_pkcs1_examples(load_vectors_from_file( + os.path.join( + "asymmetric", "RSA", "pkcs1v15crypt-vectors.txt"), + load_pkcs1_vectors + )) + ) + def test_decrypt_pkcs1v15_vectors(self, vector, backend): + private, public, example = vector + skey = rsa.RSAPrivateKey( + p=private["p"], + q=private["q"], + private_exponent=private["private_exponent"], + dmp1=private["dmp1"], + dmq1=private["dmq1"], + iqmp=private["iqmp"], + public_exponent=private["public_exponent"], + modulus=private["modulus"] + ) + ciphertext = binascii.unhexlify(example["encryption"]) + assert len(ciphertext) == math.ceil(skey.key_size / 8.0) + message = skey.decrypt( + ciphertext, + padding.PKCS1v15(), + backend + ) + assert message == binascii.unhexlify(example["message"]) + + def test_unsupported_padding(self, backend): + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING): + private_key.decrypt(b"somedata", DummyPadding(), backend) + + def test_decrypt_invalid_decrypt(self, backend): + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) + with pytest.raises(ValueError): + private_key.decrypt( + b"\x00" * 64, + padding.PKCS1v15(), + backend + ) + + def test_decrypt_ciphertext_too_large(self, backend): + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) + with pytest.raises(ValueError): + private_key.decrypt( + b"\x00" * 65, + padding.PKCS1v15(), + backend + ) + + def test_decrypt_ciphertext_too_small(self, backend): + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) + ct = binascii.unhexlify( + b"50b4c14136bd198c2f3c3ed243fce036e168d56517984a263cd66492b80804f1" + b"69d210f2b9bdfb48b12f9ea05009c77da257cc600ccefe3a6283789d8ea0" + ) + with pytest.raises(ValueError): + private_key.decrypt( + ct, + padding.PKCS1v15(), + backend + ) + + def test_rsa_decrypt_invalid_backend(self, backend): + pretend_backend = object() + private_key = rsa.RSAPrivateKey.generate(65537, 2048, backend) + + with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE): + private_key.decrypt( + b"irrelevant", + padding.PKCS1v15(), + pretend_backend + ) + + @pytest.mark.parametrize( + "vector", + _flatten_pkcs1_examples(load_vectors_from_file( + os.path.join( + "asymmetric", "RSA", "pkcs-1v2-1d2-vec", "oaep-vect.txt"), + load_pkcs1_vectors + )) + ) + def test_decrypt_oaep_vectors(self, vector, backend): + private, public, example = vector + skey = rsa.RSAPrivateKey( + p=private["p"], + q=private["q"], + private_exponent=private["private_exponent"], + dmp1=private["dmp1"], + dmq1=private["dmq1"], + iqmp=private["iqmp"], + public_exponent=private["public_exponent"], + modulus=private["modulus"] + ) + message = skey.decrypt( + binascii.unhexlify(example["encryption"]), + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA1()), + algorithm=hashes.SHA1(), + label=None + ), + backend + ) + assert message == binascii.unhexlify(example["message"]) + + def test_unsupported_oaep_mgf(self, backend): + private_key = rsa.RSAPrivateKey.generate( + public_exponent=65537, + key_size=512, + backend=backend + ) + with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_MGF): + private_key.decrypt( + b"ciphertext", + padding.OAEP( + mgf=DummyMGF(), + algorithm=hashes.SHA1(), + label=None + ), + backend + ) diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py index 2e838474..6c3f4c95 100644 --- a/tests/hazmat/primitives/utils.py +++ b/tests/hazmat/primitives/utils.py @@ -406,3 +406,21 @@ def rsa_verification_test(backend, params, hash_alg, pad_factory): verifier.verify() else: verifier.verify() + + +def _check_rsa_private_key(skey): + assert skey + assert skey.modulus + assert skey.public_exponent + assert skey.private_exponent + assert skey.p * skey.q == skey.modulus + assert skey.key_size + assert skey.dmp1 == rsa.rsa_crt_dmp1(skey.d, skey.p) + assert skey.dmq1 == rsa.rsa_crt_dmq1(skey.d, skey.q) + assert skey.iqmp == rsa.rsa_crt_iqmp(skey.p, skey.q) + + pkey = skey.public_key() + assert pkey + assert skey.modulus == pkey.modulus + assert skey.public_exponent == pkey.public_exponent + assert skey.key_size == pkey.key_size diff --git a/tests/test_utils.py b/tests/test_utils.py index c91efa7f..7a0b9e74 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -28,9 +28,10 @@ import cryptography_vectors from .utils import ( check_backend_support, check_for_iface, load_cryptrec_vectors, - load_fips_dsa_key_pair_vectors, load_fips_ecdsa_key_pair_vectors, - load_fips_ecdsa_signing_vectors, load_hash_vectors, load_nist_vectors, - load_pkcs1_vectors, load_rsa_nist_vectors, load_vectors_from_file, + load_fips_dsa_key_pair_vectors, load_fips_dsa_sig_vectors, + load_fips_ecdsa_key_pair_vectors, load_fips_ecdsa_signing_vectors, + load_hash_vectors, load_nist_vectors, load_pkcs1_vectors, + load_rsa_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm, select_backends ) @@ -1824,45 +1825,503 @@ de61329a78d526f65245380ce877e979c5b50de66c9c30d66382c8f254653d25a1eb1d3a4897d7\ assert expected == load_fips_dsa_key_pair_vectors(vector_data) -def test_vector_version(): - assert cryptography.__version__ == cryptography_vectors.__version__ - - -def test_raises_unsupported_algorithm_wrong_type(): - # Check that it raises if the wrong type of exception is raised. - class TestException(Exception): - pass - - with pytest.raises(TestException): - with raises_unsupported_algorithm(None): - raise TestException +def test_load_fips_dsa_sig_ver_vectors(): + vector_data = textwrap.dedent(""" + # CAVS 11.0 + # "SigVer" information + # Mod sizes selected: SHA-1 L=1024, N=160,SHA-384 L=2048, N=256 + # Generated on Fri Apr 01 08:37:15 2011 + + [mod = L=1024, N=160, SHA-1] + + P = dc5bf3a88b2d99e4c95cdd7a0501cc38630d425cf5c390af3429cff1f35147b795cae\ +a923f0d3577158f8a0c89dabd1962c2c453306b5d70cacfb01430aceb54e5a5fa6f93\ +40d3bd2da612fceeb76b0ec1ebfae635a56ab141b108e00dc76eefe2edd0c514c21c4\ +57457c39065dba9d0ecb7569c247172d8438ad2827b60435b + Q = e956602b83d195dbe945b3ac702fc61f81571f1d + G = d7eb9ca20a3c7a079606bafc4c9261ccaba303a5dc9fe9953f197dfe548c234895baa\ +77f441ee6a2d97b909cbbd26ff7b869d24cae51b5c6edb127a4b5d75cd8b46608bfa1\ +48249dffdb59807c5d7dde3fe3080ca3a2d28312142becb1fa8e24003e21c72871081\ +74b95d5bc711e1c8d9b1076784f5dc37a964a5e51390da713 + + Msg = 0fe1bfee500bdb76026099b1d37553f6bdfe48c82094ef98cb309dd777330bedfaa\ +2f94c823ef74ef4074b50d8706041ac0e371c7c22dcf70263b8d60e17a86c7c379c\ +fda8f22469e0df9d49d59439fc99891873628fff25dda5fac5ac794e948babdde96\ +8143ba05f1128f34fdad5875edc4cd71c6c24ba2060ffbd439ce2b3 + X = 1d93010c29ecfc432188942f46f19f44f0e1bb5d + Y = 6240ea0647117c38fe705106d56db578f3e10130928452d4f3587881b8a2bc6873a8b\ +efc3237f20914e2a91c7f07a928ee22adeed23d74ab7f82ea11f70497e578f7a9b4cb\ +d6f10226222b0b4da2ea1e49813d6bb9882fbf675c0846bb80cc891857b89b0ef1beb\ +6cce3378a9aab5d66ad4cb9277cf447dfe1e64434749432fb + R = b5af307867fb8b54390013cc67020ddf1f2c0b81 + S = 620d3b22ab5031440c3e35eab6f481298f9e9f08 + Result = P + Msg = 97d50898025d2f9ba633866e968ca75e969d394edba6517204cb3dd537c2ba38778\ +a2dc9dbc685a915e5676fcd43bc3726bc59ce3d7a9fae35565082a069c139fa37c9\ +0d922b126933db3fa6c5ef6b1edf00d174a51887bb76909c6a94fe994ecc7b7fc8f\ +26113b17f30f9d01693df99a125b4f17e184331c6b6e8ca00f54f3a + X = 350e13534692a7e0c4b7d58836046c436fbb2322 + Y = 69974de550fe6bd3099150faea1623ad3fb6d9bf23a07215093f319725ad0877accff\ +d291b6da18eb0cbe51676ceb0977504eb97c27c0b191883f72fb2710a9fbd8bcf13be\ +0bf854410b32f42b33ec89d3cc1cf892bcd536c4195ca9ada302ad600c3408739935d\ +77dc247529ca47f844cc86f5016a2fe962c6e20ca7c4d4e8f + R = b5d05faa7005764e8dae0327c5bf1972ff7681b9 + S = 18ea15bd9f00475b25204cbc23f8c23e01588015 + Result = F (3 - R changed ) + + [mod = L=2048, N=224, SHA-1] + + # unsupported so we ignore this + + Msg = f9d01693df99a125b4f17e184331c6b6e8ca00f54f3a + X = e0c4b7d58836046c436fbb2322 + Y = fb6d9bf23a07215093f319725ad0877accff + R = 5764e8dae0327c5bf1972ff7681b9 + S = 475b25204cbc23f8c23e01588015 + Result = F (3 - R changed ) + + [mod = L=2048, N=256, SHA-384] + + P = e7c1c86125db9ef417da1ced7ea0861bdad629216a3f3c745df42a46b989e59f4d984\ +25ee3c932fa3c2b6f637bdb6545bec526faa037e11f5578a4363b9fca5eba60d6a9cb\ +aa2befd04141d989c7356285132c2eaf74f2d868521cdc0a17ae9a2546ef863027d3f\ +8cc7949631fd0e2971417a912c8b8c5c989730db6ea6e8baee0e667850429038093c8\ +51ccb6fb173bb081e0efe0bd7450e0946888f89f75e443ab93ef2da293a01622cf43c\ +6dd79625d41ba8f9ef7e3086ab39134283d8e96c89249488120fd061e4a87d34af410\ +69c0b4fd3934c31b589cbe85b68b912718d5dab859fda7082511fad1d152044905005\ +546e19b14aa96585a55269bf2b831 + Q = 8e056ec9d4b7acb580087a6ed9ba3478711bb025d5b8d9c731ef9b38bd43db2f + G = dc2bfb9776786ad310c8b0cdcbba3062402613c67e6959a8d8d1b05aab636528b7b1f\ +e9cd33765f853d6dbe13d09f2681f8c7b1ed7886aaed70c7bd76dbe858ffb8bd86235\ +ddf759244678f428c6519af593dc94eeadbd9852ba2b3d61664e8d58c29d2039af3c3\ +d6d16f90988f6a8c824569f3d48050e30896a9e17cd0232ef01ab8790008f6973b84c\ +763a72f4ae8b485abfb7e8efeb86808fa2b281d3e5d65d28f5992a34c077c5aa8026c\ +b2fbc34a45f7e9bd216b10e6f12ecb172e9a6eb8f2e91316905b6add1fd22e83bc2f0\ +89f1d5e6a6e6707c18ff55ddcb7954e8bceaf0efc4e8314910c03b0e51175f344faaf\ +ee476a373ac95743cec712b72cf2e + + Msg = 6cd6ccfd66bcd832189c5f0c77994210e3bf2c43416f0fe77c4e92f31c5369538dc\ +2c003f146c5ac79df43194ccf3c44d470d9f1083bd15b99b5bcf88c32d8a9021f09\ +ea2288d7b3bf345a12aef3949c1e121b9fb371a67c2d1377364206ac839dd784835\ +61426bda0303f285aa12e9c45d3cdfc6beae3549703b187deeb3296 + X = 56c897b5938ad5b3d437d7e4826da586a6b3be15e893fa1aaa946f20a028b6b3 + Y = 38ad44489e1a5778b9689f4dcf40e2acf23840fb954e987d6e8cb629106328ac64e1f\ +3c3eba48b21176ad4afe3b733bead382ee1597e1b83e4b43424f2daaba04e5bd79e14\ +36693ac2bddb79a298f026e57e200a252efd1e848a4a2e90be6e78f5242b468b9c0c6\ +d2615047a5a40b9ae7e57a519114db55bf3bed65e580f894b094630ca9c217f6accd0\ +91e72d2f22da620044ff372d7273f9445017fad492959e59600b7494dbe766a03e401\ +25d4e6747c76f68a5b0cdc0e7d7cee12d08c6fb7d0fb049e420a33405075ed4463296\ +345ca695fb7feab7c1b5333ae519fcd4bb6a043f4555378969114743d4face96cad31\ +c0e0089da4e3f61b6d7dabc088ab7 + R = 3b85b17be240ed658beb3652c9d93e8e9eea160d35ee2459614305802963374e + S = 726800a5174a53b56dce86064109c0273cd11fcfa3c92c5cd6aa910260c0e3c7 + Result = F (1 - Message changed) + + Msg = 3ad6b0884f358dea09c31a9abc40c45a6000611fc2b907b30eac00413fd2819de70\ +15488a411609d46c499b8f7afa1b78b352ac7f8535bd805b8ff2a5eae557098c668\ +f7ccd73af886d6823a6d456c29931ee864ed46d767382785728c2a83fcff5271007\ +d2a67d06fa205fd7b9d1a42ea5d6dc76e5e18a9eb148cd1e8b262ae + X = 2faf566a9f057960f1b50c69508f483d9966d6e35743591f3a677a9dc40e1555 + Y = 926425d617babe87c442b03903e32ba5bbf0cd9d602b59c4df791a4d64a6d4333ca0c\ +0d370552539197d327dcd1bbf8c454f24b03fc7805f862db34c7b066ddfddbb11dbd0\ +10b27123062d028fe041cb56a2e77488348ae0ab6705d87aac4d4e9e6600e9e706326\ +d9979982cffa839beb9eacc3963bcca455a507e80c1c37ad4e765b2c9c0477a075e9b\ +c584feacdf3a35a9391d4711f14e197c54022282bfed9a191213d64127f17a9c5affe\ +c26e0c71f15d3a5b16098fec118c45bf8bb2f3b1560df0949254c1c0aeb0a16d5a95a\ +40fab8521fbe8ea77c51169b587cc3360e5733e6a23b9fded8c40724ea1f9e93614b3\ +a6c9b4f8dbbe915b794497227ba62 + R = 343ea0a9e66277380f604d5880fca686bffab69ca97bfba015a102a7e23dce0e + S = 6258488c770e0f5ad7b9da8bade5023fc0d17c6ec517bd08d53e6dc01ac5c2b3 + Result = P + """).splitlines() -def test_raises_unsupported_algorithm_wrong_reason(): - # Check that it fails if the wrong reason code is raised. - with pytest.raises(AssertionError): - with raises_unsupported_algorithm(None): - raise UnsupportedAlgorithm("An error.", - _Reasons.BACKEND_MISSING_INTERFACE) + expected = [ + { + 'p': int('dc5bf3a88b2d99e4c95cdd7a0501cc38630d425cf5c390af3429cff1' + 'f35147b795caea923f0d3577158f8a0c89dabd1962c2c453306b5d70' + 'cacfb01430aceb54e5a5fa6f9340d3bd2da612fceeb76b0ec1ebfae6' + '35a56ab141b108e00dc76eefe2edd0c514c21c457457c39065dba9d0' + 'ecb7569c247172d8438ad2827b60435b', 16), + 'q': int('e956602b83d195dbe945b3ac702fc61f81571f1d', 16), + 'g': int('d7eb9ca20a3c7a079606bafc4c9261ccaba303a5dc9fe9953f197dfe' + '548c234895baa77f441ee6a2d97b909cbbd26ff7b869d24cae51b5c6' + 'edb127a4b5d75cd8b46608bfa148249dffdb59807c5d7dde3fe3080c' + 'a3a2d28312142becb1fa8e24003e21c7287108174b95d5bc711e1c8d' + '9b1076784f5dc37a964a5e51390da713', 16), + 'digest_algorithm': 'SHA-1', + 'msg': binascii.unhexlify( + b'0fe1bfee500bdb76026099b1d37553f6bdfe48c82094ef98cb309dd77733' + b'0bedfaa2f94c823ef74ef4074b50d8706041ac0e371c7c22dcf70263b8d6' + b'0e17a86c7c379cfda8f22469e0df9d49d59439fc99891873628fff25dda5' + b'fac5ac794e948babdde968143ba05f1128f34fdad5875edc4cd71c6c24ba' + b'2060ffbd439ce2b3'), + 'x': int('1d93010c29ecfc432188942f46f19f44f0e1bb5d', 16), + 'y': int('6240ea0647117c38fe705106d56db578f3e10130928452d4f3587881' + 'b8a2bc6873a8befc3237f20914e2a91c7f07a928ee22adeed23d74ab' + '7f82ea11f70497e578f7a9b4cbd6f10226222b0b4da2ea1e49813d6b' + 'b9882fbf675c0846bb80cc891857b89b0ef1beb6cce3378a9aab5d66' + 'ad4cb9277cf447dfe1e64434749432fb', 16), + 'r': int('b5af307867fb8b54390013cc67020ddf1f2c0b81', 16), + 's': int('620d3b22ab5031440c3e35eab6f481298f9e9f08', 16), + 'result': 'P'}, + { + 'p': int('dc5bf3a88b2d99e4c95cdd7a0501cc38630d425cf5c390af3429cff1' + 'f35147b795caea923f0d3577158f8a0c89dabd1962c2c453306b5d70' + 'cacfb01430aceb54e5a5fa6f9340d3bd2da612fceeb76b0ec1ebfae6' + '35a56ab141b108e00dc76eefe2edd0c514c21c457457c39065dba9d0' + 'ecb7569c247172d8438ad2827b60435b', 16), + 'q': int('e956602b83d195dbe945b3ac702fc61f81571f1d', 16), + 'g': int('d7eb9ca20a3c7a079606bafc4c9261ccaba303a5dc9fe9953f197dfe' + '548c234895baa77f441ee6a2d97b909cbbd26ff7b869d24cae51b5c6' + 'edb127a4b5d75cd8b46608bfa148249dffdb59807c5d7dde3fe3080c' + 'a3a2d28312142becb1fa8e24003e21c7287108174b95d5bc711e1c8d' + '9b1076784f5dc37a964a5e51390da713', 16), + 'digest_algorithm': 'SHA-1', + 'msg': binascii.unhexlify( + b'97d50898025d2f9ba633866e968ca75e969d394edba6517204cb3dd537c2' + b'ba38778a2dc9dbc685a915e5676fcd43bc3726bc59ce3d7a9fae35565082' + b'a069c139fa37c90d922b126933db3fa6c5ef6b1edf00d174a51887bb7690' + b'9c6a94fe994ecc7b7fc8f26113b17f30f9d01693df99a125b4f17e184331' + b'c6b6e8ca00f54f3a'), + 'x': int('350e13534692a7e0c4b7d58836046c436fbb2322', 16), + 'y': int('69974de550fe6bd3099150faea1623ad3fb6d9bf23a07215093f3197' + '25ad0877accffd291b6da18eb0cbe51676ceb0977504eb97c27c0b19' + '1883f72fb2710a9fbd8bcf13be0bf854410b32f42b33ec89d3cc1cf8' + '92bcd536c4195ca9ada302ad600c3408739935d77dc247529ca47f84' + '4cc86f5016a2fe962c6e20ca7c4d4e8f', 16), + 'r': int('b5d05faa7005764e8dae0327c5bf1972ff7681b9', 16), + 's': int('18ea15bd9f00475b25204cbc23f8c23e01588015', 16), + 'result': 'F'}, + { + 'p': int('e7c1c86125db9ef417da1ced7ea0861bdad629216a3f3c745df42a4' + '6b989e59f4d98425ee3c932fa3c2b6f637bdb6545bec526faa037e1' + '1f5578a4363b9fca5eba60d6a9cbaa2befd04141d989c7356285132' + 'c2eaf74f2d868521cdc0a17ae9a2546ef863027d3f8cc7949631fd0' + 'e2971417a912c8b8c5c989730db6ea6e8baee0e667850429038093c' + '851ccb6fb173bb081e0efe0bd7450e0946888f89f75e443ab93ef2d' + 'a293a01622cf43c6dd79625d41ba8f9ef7e3086ab39134283d8e96c' + '89249488120fd061e4a87d34af41069c0b4fd3934c31b589cbe85b6' + '8b912718d5dab859fda7082511fad1d152044905005546e19b14aa9' + '6585a55269bf2b831', 16), + 'q': int('8e056ec9d4b7acb580087a6ed9ba3478711bb025d5b8d9c731ef9b3' + '8bd43db2f', 16), + 'g': int('dc2bfb9776786ad310c8b0cdcbba3062402613c67e6959a8d8d1b05' + 'aab636528b7b1fe9cd33765f853d6dbe13d09f2681f8c7b1ed7886a' + 'aed70c7bd76dbe858ffb8bd86235ddf759244678f428c6519af593d' + 'c94eeadbd9852ba2b3d61664e8d58c29d2039af3c3d6d16f90988f6' + 'a8c824569f3d48050e30896a9e17cd0232ef01ab8790008f6973b84' + 'c763a72f4ae8b485abfb7e8efeb86808fa2b281d3e5d65d28f5992a' + '34c077c5aa8026cb2fbc34a45f7e9bd216b10e6f12ecb172e9a6eb8' + 'f2e91316905b6add1fd22e83bc2f089f1d5e6a6e6707c18ff55ddcb' + '7954e8bceaf0efc4e8314910c03b0e51175f344faafee476a373ac9' + '5743cec712b72cf2e', 16), + 'digest_algorithm': 'SHA-384', + 'msg': binascii.unhexlify( + b'6cd6ccfd66bcd832189c5f0c77994210e3bf2c43416f0fe77c4e92f31c5' + b'369538dc2c003f146c5ac79df43194ccf3c44d470d9f1083bd15b99b5bc' + b'f88c32d8a9021f09ea2288d7b3bf345a12aef3949c1e121b9fb371a67c2' + b'd1377364206ac839dd78483561426bda0303f285aa12e9c45d3cdfc6bea' + b'e3549703b187deeb3296'), + 'x': int('56c897b5938ad5b3d437d7e4826da586a6b3be15e893fa1aaa946f2' + '0a028b6b3', 16), + 'y': int('38ad44489e1a5778b9689f4dcf40e2acf23840fb954e987d6e8cb62' + '9106328ac64e1f3c3eba48b21176ad4afe3b733bead382ee1597e1b' + '83e4b43424f2daaba04e5bd79e1436693ac2bddb79a298f026e57e2' + '00a252efd1e848a4a2e90be6e78f5242b468b9c0c6d2615047a5a40' + 'b9ae7e57a519114db55bf3bed65e580f894b094630ca9c217f6accd' + '091e72d2f22da620044ff372d7273f9445017fad492959e59600b74' + '94dbe766a03e40125d4e6747c76f68a5b0cdc0e7d7cee12d08c6fb7' + 'd0fb049e420a33405075ed4463296345ca695fb7feab7c1b5333ae5' + '19fcd4bb6a043f4555378969114743d4face96cad31c0e0089da4e3' + 'f61b6d7dabc088ab7', 16), + 'r': int('3b85b17be240ed658beb3652c9d93e8e9eea160d35ee24596143058' + '02963374e', 16), + 's': int('726800a5174a53b56dce86064109c0273cd11fcfa3c92c5cd6aa910' + '260c0e3c7', 16), + 'result': 'F'}, + { + 'p': int('e7c1c86125db9ef417da1ced7ea0861bdad629216a3f3c745df42a4' + '6b989e59f4d98425ee3c932fa3c2b6f637bdb6545bec526faa037e1' + '1f5578a4363b9fca5eba60d6a9cbaa2befd04141d989c7356285132' + 'c2eaf74f2d868521cdc0a17ae9a2546ef863027d3f8cc7949631fd0' + 'e2971417a912c8b8c5c989730db6ea6e8baee0e667850429038093c' + '851ccb6fb173bb081e0efe0bd7450e0946888f89f75e443ab93ef2d' + 'a293a01622cf43c6dd79625d41ba8f9ef7e3086ab39134283d8e96c' + '89249488120fd061e4a87d34af41069c0b4fd3934c31b589cbe85b6' + '8b912718d5dab859fda7082511fad1d152044905005546e19b14aa9' + '6585a55269bf2b831', 16), + 'q': int('8e056ec9d4b7acb580087a6ed9ba3478711bb025d5b8d9c731ef9b3' + '8bd43db2f', 16), + 'g': int('dc2bfb9776786ad310c8b0cdcbba3062402613c67e6959a8d8d1b05' + 'aab636528b7b1fe9cd33765f853d6dbe13d09f2681f8c7b1ed7886a' + 'aed70c7bd76dbe858ffb8bd86235ddf759244678f428c6519af593d' + 'c94eeadbd9852ba2b3d61664e8d58c29d2039af3c3d6d16f90988f6' + 'a8c824569f3d48050e30896a9e17cd0232ef01ab8790008f6973b84' + 'c763a72f4ae8b485abfb7e8efeb86808fa2b281d3e5d65d28f5992a' + '34c077c5aa8026cb2fbc34a45f7e9bd216b10e6f12ecb172e9a6eb8' + 'f2e91316905b6add1fd22e83bc2f089f1d5e6a6e6707c18ff55ddcb' + '7954e8bceaf0efc4e8314910c03b0e51175f344faafee476a373ac9' + '5743cec712b72cf2e', 16), + 'digest_algorithm': 'SHA-384', + 'msg': binascii.unhexlify( + b'3ad6b0884f358dea09c31a9abc40c45a6000611fc2b907b30eac00413fd' + b'2819de7015488a411609d46c499b8f7afa1b78b352ac7f8535bd805b8ff' + b'2a5eae557098c668f7ccd73af886d6823a6d456c29931ee864ed46d7673' + b'82785728c2a83fcff5271007d2a67d06fa205fd7b9d1a42ea5d6dc76e5e' + b'18a9eb148cd1e8b262ae'), + 'x': int('2faf566a9f057960f1b50c69508f483d9966d6e35743591f3a677a9' + 'dc40e1555', 16), + 'y': int('926425d617babe87c442b03903e32ba5bbf0cd9d602b59c4df791a4d' + '64a6d4333ca0c0d370552539197d327dcd1bbf8c454f24b03fc7805f' + '862db34c7b066ddfddbb11dbd010b27123062d028fe041cb56a2e774' + '88348ae0ab6705d87aac4d4e9e6600e9e706326d9979982cffa839be' + 'b9eacc3963bcca455a507e80c1c37ad4e765b2c9c0477a075e9bc584' + 'feacdf3a35a9391d4711f14e197c54022282bfed9a191213d64127f1' + '7a9c5affec26e0c71f15d3a5b16098fec118c45bf8bb2f3b1560df09' + '49254c1c0aeb0a16d5a95a40fab8521fbe8ea77c51169b587cc3360e' + '5733e6a23b9fded8c40724ea1f9e93614b3a6c9b4f8dbbe915b79449' + '7227ba62', 16), + 'r': int('343ea0a9e66277380f604d5880fca686bffab69ca97bfba015a102a' + '7e23dce0e', 16), + 's': int('6258488c770e0f5ad7b9da8bade5023fc0d17c6ec517bd08d53e6dc' + '01ac5c2b3', 16), + 'result': 'P'} + ] + assert expected == load_fips_dsa_sig_vectors(vector_data) -def test_raises_unsupported_no_exc(): - # Check that it fails if no exception is raised. - with pytest.raises(pytest.fail.Exception): - with raises_unsupported_algorithm( - _Reasons.BACKEND_MISSING_INTERFACE - ): - pass +def test_load_fips_dsa_sig_gen_vectors(): + vector_data = textwrap.dedent(""" + # CAVS 11.2 + # "SigGen" information for "dsa2_values" + # Mod sizes selected: SHA-1 L=1024, N=160, SHA-256 L=2048, N=256 + + [mod = L=1024, N=160, SHA-1] + + P = a8f9cd201e5e35d892f85f80e4db2599a5676a3b1d4f190330ed3256b26d0e80a0e49\ +a8fffaaad2a24f472d2573241d4d6d6c7480c80b4c67bb4479c15ada7ea8424d2502fa01472e7\ +60241713dab025ae1b02e1703a1435f62ddf4ee4c1b664066eb22f2e3bf28bb70a2a76e4fd5eb\ +e2d1229681b5b06439ac9c7e9d8bde283 + Q = f85f0f83ac4df7ea0cdf8f469bfeeaea14156495 + G = 2b3152ff6c62f14622b8f48e59f8af46883b38e79b8c74deeae9df131f8b856e3ad6c\ +8455dab87cc0da8ac973417ce4f7878557d6cdf40b35b4a0ca3eb310c6a95d68ce284ad4e25ea\ +28591611ee08b8444bd64b25f3f7c572410ddfb39cc728b9c936f85f419129869929cdb909a6a\ +3a99bbe089216368171bd0ba81de4fe33 + + Msg = 3b46736d559bd4e0c2c1b2553a33ad3c6cf23cac998d3d0c0e8fa4b19bca06f2f38\ +6db2dcff9dca4f40ad8f561ffc308b46c5f31a7735b5fa7e0f9e6cb512e63d7eea05538d66a75\ +cd0d4234b5ccf6c1715ccaaf9cdc0a2228135f716ee9bdee7fc13ec27a03a6d11c5c5b3685f51\ +900b1337153bc6c4e8f52920c33fa37f4e7 + Y = 313fd9ebca91574e1c2eebe1517c57e0c21b0209872140c5328761bbb2450b33f1b18\ +b409ce9ab7c4cd8fda3391e8e34868357c199e16a6b2eba06d6749def791d79e95d3a4d09b24c\ +392ad89dbf100995ae19c01062056bb14bce005e8731efde175f95b975089bdcdaea562b32786\ +d96f5a31aedf75364008ad4fffebb970b + R = 50ed0e810e3f1c7cb6ac62332058448bd8b284c0 + S = c6aded17216b46b7e4b6f2a97c1ad7cc3da83fde + + Msg = d2bcb53b044b3e2e4b61ba2f91c0995fb83a6a97525e66441a3b489d9594238bc74\ +0bdeea0f718a769c977e2de003877b5d7dc25b182ae533db33e78f2c3ff0645f2137abc137d4e\ +7d93ccf24f60b18a820bc07c7b4b5fe08b4f9e7d21b256c18f3b9d49acc4f93e2ce6f3754c780\ +7757d2e1176042612cb32fc3f4f70700e25 + Y = 29bdd759aaa62d4bf16b4861c81cf42eac2e1637b9ecba512bdbc13ac12a80ae8de25\ +26b899ae5e4a231aef884197c944c732693a634d7659abc6975a773f8d3cd5a361fe2492386a3\ +c09aaef12e4a7e73ad7dfc3637f7b093f2c40d6223a195c136adf2ea3fbf8704a675aa7817aa7\ +ec7f9adfb2854d4e05c3ce7f76560313b + R = a26c00b5750a2d27fe7435b93476b35438b4d8ab + S = 61c9bfcb2938755afa7dad1d1e07c6288617bf70 + + [mod = L=2048, N=256, SHA-256] + + P = a8adb6c0b4cf9588012e5deff1a871d383e0e2a85b5e8e03d814fe13a059705e66323\ +0a377bf7323a8fa117100200bfd5adf857393b0bbd67906c081e585410e38480ead51684dac3a\ +38f7b64c9eb109f19739a4517cd7d5d6291e8af20a3fbf17336c7bf80ee718ee087e322ee4104\ +7dabefbcc34d10b66b644ddb3160a28c0639563d71993a26543eadb7718f317bf5d9577a61565\ +61b082a10029cd44012b18de6844509fe058ba87980792285f2750969fe89c2cd6498db354563\ +8d5379d125dccf64e06c1af33a6190841d223da1513333a7c9d78462abaab31b9f96d5f34445c\ +eb6309f2f6d2c8dde06441e87980d303ef9a1ff007e8be2f0be06cc15f + Q = e71f8567447f42e75f5ef85ca20fe557ab0343d37ed09edc3f6e68604d6b9dfb + G = 5ba24de9607b8998e66ce6c4f812a314c6935842f7ab54cd82b19fa104abfb5d84579\ +a623b2574b37d22ccae9b3e415e48f5c0f9bcbdff8071d63b9bb956e547af3a8df99e5d306197\ +9652ff96b765cb3ee493643544c75dbe5bb39834531952a0fb4b0378b3fcbb4c8b5800a533039\ +2a2a04e700bb6ed7e0b85795ea38b1b962741b3f33b9dde2f4ec1354f09e2eb78e95f037a5804\ +b6171659f88715ce1a9b0cc90c27f35ef2f10ff0c7c7a2bb0154d9b8ebe76a3d764aa879af372\ +f4240de8347937e5a90cec9f41ff2f26b8da9a94a225d1a913717d73f10397d2183f1ba3b7b45\ +a68f1ff1893caf69a827802f7b6a48d51da6fbefb64fd9a6c5b75c4561 + + Msg = 4e3a28bcf90d1d2e75f075d9fbe55b36c5529b17bc3a9ccaba6935c9e20548255b3\ +dfae0f91db030c12f2c344b3a29c4151c5b209f5e319fdf1c23b190f64f1fe5b330cb7c8fa952\ +f9d90f13aff1cb11d63181da9efc6f7e15bfed4862d1a62c7dcf3ba8bf1ff304b102b1ec3f149\ +7dddf09712cf323f5610a9d10c3d9132659 + Y = 5a55dceddd1134ee5f11ed85deb4d634a3643f5f36dc3a70689256469a0b651ad2288\ +0f14ab85719434f9c0e407e60ea420e2a0cd29422c4899c416359dbb1e592456f2b3cce233259\ +c117542fd05f31ea25b015d9121c890b90e0bad033be1368d229985aac7226d1c8c2eab325ef3\ +b2cd59d3b9f7de7dbc94af1a9339eb430ca36c26c46ecfa6c5481711496f624e188ad7540ef5d\ +f26f8efacb820bd17a1f618acb50c9bc197d4cb7ccac45d824a3bf795c234b556b06aeb929173\ +453252084003f69fe98045fe74002ba658f93475622f76791d9b2623d1b5fff2cc16844746efd\ +2d30a6a8134bfc4c8cc80a46107901fb973c28fc553130f3286c1489da + R = 633055e055f237c38999d81c397848c38cce80a55b649d9e7905c298e2a51447 + S = 2bbf68317660ec1e4b154915027b0bc00ee19cfc0bf75d01930504f2ce10a8b0 + + Msg = a733b3f588d5ac9b9d4fe2f804df8c256403a9f8eef6f191fc48e1267fb5b4d546b\ +a11e77b667844e489bf0d5f72990aeb061d01ccd7949a23def74a803b7d92d51abfadeb4885ff\ +d8ffd58ab87548a15c087a39b8993b2fa64c9d31a594eeb7512da16955834336a234435c5a9d0\ +dd9b15a94e116154dea63fdc8dd7a512181 + Y = 356ed47537fbf02cb30a8cee0537f300dff1d0c467399ce70b87a8758d5ec9dd25624\ +6fccaeb9dfe109f2a984f2ddaa87aad54ce0d31f907e504521baf4207d7073b0a4a9fc67d8ddd\ +a99f87aed6e0367cec27f9c608af743bf1ee6e11d55a182d43b024ace534029b866f6422828bb\ +81a39aae9601ee81c7f81dd358e69f4e2edfa4654d8a65bc64311dc86aac4abc1fc7a3f651596\ +61a0d8e288eb8d665cb0adf5ac3d6ba8e9453facf7542393ae24fd50451d3828086558f7ec528\ +e284935a53f67a1aa8e25d8ad5c4ad55d83aef883a4d9eeb6297e6a53f65049ba9e2c6b7953a7\ +60bc1dc46f78ceaaa2c02f5375dd82e708744aa40b15799eb81d7e5b1a + R = bcd490568c0a89ba311bef88ea4f4b03d273e793722722327095a378dd6f3522 + S = 74498fc43091fcdd2d1ef0775f8286945a01cd72b805256b0451f9cbd943cf82 + """).splitlines() -def test_raises_unsupported_algorithm(): - # Check that it doesnt assert if the right things are raised. - with raises_unsupported_algorithm( - _Reasons.BACKEND_MISSING_INTERFACE - ) as exc_info: - raise UnsupportedAlgorithm("An error.", - _Reasons.BACKEND_MISSING_INTERFACE) - assert exc_info.type is UnsupportedAlgorithm + expected = [ + { + 'p': int('a8f9cd201e5e35d892f85f80e4db2599a5676a3b1d4f190330ed325' + '6b26d0e80a0e49a8fffaaad2a24f472d2573241d4d6d6c7480c80b4' + 'c67bb4479c15ada7ea8424d2502fa01472e760241713dab025ae1b0' + '2e1703a1435f62ddf4ee4c1b664066eb22f2e3bf28bb70a2a76e4fd' + '5ebe2d1229681b5b06439ac9c7e9d8bde283', 16), + 'q': int('f85f0f83ac4df7ea0cdf8f469bfeeaea14156495', 16), + 'g': int('2b3152ff6c62f14622b8f48e59f8af46883b38e79b8c74deeae9df1' + '31f8b856e3ad6c8455dab87cc0da8ac973417ce4f7878557d6cdf40' + 'b35b4a0ca3eb310c6a95d68ce284ad4e25ea28591611ee08b8444bd' + '64b25f3f7c572410ddfb39cc728b9c936f85f419129869929cdb909' + 'a6a3a99bbe089216368171bd0ba81de4fe33', 16), + 'digest_algorithm': 'SHA-1', + 'msg': binascii.unhexlify( + b'3b46736d559bd4e0c2c1b2553a33ad3c6cf23cac998d3d0c0e8fa4b19bc' + b'a06f2f386db2dcff9dca4f40ad8f561ffc308b46c5f31a7735b5fa7e0f9' + b'e6cb512e63d7eea05538d66a75cd0d4234b5ccf6c1715ccaaf9cdc0a222' + b'8135f716ee9bdee7fc13ec27a03a6d11c5c5b3685f51900b1337153bc6c' + b'4e8f52920c33fa37f4e7'), + 'y': int('313fd9ebca91574e1c2eebe1517c57e0c21b0209872140c5328761b' + 'bb2450b33f1b18b409ce9ab7c4cd8fda3391e8e34868357c199e16a' + '6b2eba06d6749def791d79e95d3a4d09b24c392ad89dbf100995ae1' + '9c01062056bb14bce005e8731efde175f95b975089bdcdaea562b32' + '786d96f5a31aedf75364008ad4fffebb970b', 16), + 'r': int('50ed0e810e3f1c7cb6ac62332058448bd8b284c0', 16), + 's': int('c6aded17216b46b7e4b6f2a97c1ad7cc3da83fde', 16)}, + { + 'p': int('a8f9cd201e5e35d892f85f80e4db2599a5676a3b1d4f190330ed325' + '6b26d0e80a0e49a8fffaaad2a24f472d2573241d4d6d6c7480c80b4' + 'c67bb4479c15ada7ea8424d2502fa01472e760241713dab025ae1b0' + '2e1703a1435f62ddf4ee4c1b664066eb22f2e3bf28bb70a2a76e4fd' + '5ebe2d1229681b5b06439ac9c7e9d8bde283', 16), + 'q': int('f85f0f83ac4df7ea0cdf8f469bfeeaea14156495', 16), + 'g': int('2b3152ff6c62f14622b8f48e59f8af46883b38e79b8c74deeae9df1' + '31f8b856e3ad6c8455dab87cc0da8ac973417ce4f7878557d6cdf40' + 'b35b4a0ca3eb310c6a95d68ce284ad4e25ea28591611ee08b8444bd' + '64b25f3f7c572410ddfb39cc728b9c936f85f419129869929cdb909' + 'a6a3a99bbe089216368171bd0ba81de4fe33', 16), + 'digest_algorithm': 'SHA-1', + 'msg': binascii.unhexlify( + b'd2bcb53b044b3e2e4b61ba2f91c0995fb83a6a97525e66441a3b489d959' + b'4238bc740bdeea0f718a769c977e2de003877b5d7dc25b182ae533db33e' + b'78f2c3ff0645f2137abc137d4e7d93ccf24f60b18a820bc07c7b4b5fe08' + b'b4f9e7d21b256c18f3b9d49acc4f93e2ce6f3754c7807757d2e11760426' + b'12cb32fc3f4f70700e25'), + 'y': int('29bdd759aaa62d4bf16b4861c81cf42eac2e1637b9ecba512bdbc13' + 'ac12a80ae8de2526b899ae5e4a231aef884197c944c732693a634d7' + '659abc6975a773f8d3cd5a361fe2492386a3c09aaef12e4a7e73ad7' + 'dfc3637f7b093f2c40d6223a195c136adf2ea3fbf8704a675aa7817' + 'aa7ec7f9adfb2854d4e05c3ce7f76560313b', 16), + 'r': int('a26c00b5750a2d27fe7435b93476b35438b4d8ab', 16), + 's': int('61c9bfcb2938755afa7dad1d1e07c6288617bf70', 16)}, + { + 'p': int('a8adb6c0b4cf9588012e5deff1a871d383e0e2a85b5e8e03d814fe1' + '3a059705e663230a377bf7323a8fa117100200bfd5adf857393b0bb' + 'd67906c081e585410e38480ead51684dac3a38f7b64c9eb109f1973' + '9a4517cd7d5d6291e8af20a3fbf17336c7bf80ee718ee087e322ee4' + '1047dabefbcc34d10b66b644ddb3160a28c0639563d71993a26543e' + 'adb7718f317bf5d9577a6156561b082a10029cd44012b18de684450' + '9fe058ba87980792285f2750969fe89c2cd6498db3545638d5379d1' + '25dccf64e06c1af33a6190841d223da1513333a7c9d78462abaab31' + 'b9f96d5f34445ceb6309f2f6d2c8dde06441e87980d303ef9a1ff00' + '7e8be2f0be06cc15f', 16), + 'q': int('e71f8567447f42e75f5ef85ca20fe557ab0343d37ed09edc3f6e686' + '04d6b9dfb', 16), + 'g': int('5ba24de9607b8998e66ce6c4f812a314c6935842f7ab54cd82b19fa' + '104abfb5d84579a623b2574b37d22ccae9b3e415e48f5c0f9bcbdff' + '8071d63b9bb956e547af3a8df99e5d3061979652ff96b765cb3ee49' + '3643544c75dbe5bb39834531952a0fb4b0378b3fcbb4c8b5800a533' + '0392a2a04e700bb6ed7e0b85795ea38b1b962741b3f33b9dde2f4ec' + '1354f09e2eb78e95f037a5804b6171659f88715ce1a9b0cc90c27f3' + '5ef2f10ff0c7c7a2bb0154d9b8ebe76a3d764aa879af372f4240de8' + '347937e5a90cec9f41ff2f26b8da9a94a225d1a913717d73f10397d' + '2183f1ba3b7b45a68f1ff1893caf69a827802f7b6a48d51da6fbefb' + '64fd9a6c5b75c4561', 16), + 'digest_algorithm': 'SHA-256', + 'msg': binascii.unhexlify( + b'4e3a28bcf90d1d2e75f075d9fbe55b36c5529b17bc3a9ccaba6935c9e20' + b'548255b3dfae0f91db030c12f2c344b3a29c4151c5b209f5e319fdf1c23' + b'b190f64f1fe5b330cb7c8fa952f9d90f13aff1cb11d63181da9efc6f7e1' + b'5bfed4862d1a62c7dcf3ba8bf1ff304b102b1ec3f1497dddf09712cf323' + b'f5610a9d10c3d9132659'), + 'y': int('5a55dceddd1134ee5f11ed85deb4d634a3643f5f36dc3a706892564' + '69a0b651ad22880f14ab85719434f9c0e407e60ea420e2a0cd29422' + 'c4899c416359dbb1e592456f2b3cce233259c117542fd05f31ea25b' + '015d9121c890b90e0bad033be1368d229985aac7226d1c8c2eab325' + 'ef3b2cd59d3b9f7de7dbc94af1a9339eb430ca36c26c46ecfa6c548' + '1711496f624e188ad7540ef5df26f8efacb820bd17a1f618acb50c9' + 'bc197d4cb7ccac45d824a3bf795c234b556b06aeb92917345325208' + '4003f69fe98045fe74002ba658f93475622f76791d9b2623d1b5fff' + '2cc16844746efd2d30a6a8134bfc4c8cc80a46107901fb973c28fc5' + '53130f3286c1489da', 16), + 'r': int('633055e055f237c38999d81c397848c38cce80a55b649d9e7905c29' + '8e2a51447', 16), + 's': int('2bbf68317660ec1e4b154915027b0bc00ee19cfc0bf75d01930504f' + '2ce10a8b0', 16)}, + { + 'p': int('a8adb6c0b4cf9588012e5deff1a871d383e0e2a85b5e8e03d814fe1' + '3a059705e663230a377bf7323a8fa117100200bfd5adf857393b0bb' + 'd67906c081e585410e38480ead51684dac3a38f7b64c9eb109f1973' + '9a4517cd7d5d6291e8af20a3fbf17336c7bf80ee718ee087e322ee4' + '1047dabefbcc34d10b66b644ddb3160a28c0639563d71993a26543e' + 'adb7718f317bf5d9577a6156561b082a10029cd44012b18de684450' + '9fe058ba87980792285f2750969fe89c2cd6498db3545638d5379d1' + '25dccf64e06c1af33a6190841d223da1513333a7c9d78462abaab31' + 'b9f96d5f34445ceb6309f2f6d2c8dde06441e87980d303ef9a1ff00' + '7e8be2f0be06cc15f', 16), + 'q': int('e71f8567447f42e75f5ef85ca20fe557ab0343d37ed09edc3f6e686' + '04d6b9dfb', 16), + 'g': int('5ba24de9607b8998e66ce6c4f812a314c6935842f7ab54cd82b19fa' + '104abfb5d84579a623b2574b37d22ccae9b3e415e48f5c0f9bcbdff' + '8071d63b9bb956e547af3a8df99e5d3061979652ff96b765cb3ee49' + '3643544c75dbe5bb39834531952a0fb4b0378b3fcbb4c8b5800a533' + '0392a2a04e700bb6ed7e0b85795ea38b1b962741b3f33b9dde2f4ec' + '1354f09e2eb78e95f037a5804b6171659f88715ce1a9b0cc90c27f3' + '5ef2f10ff0c7c7a2bb0154d9b8ebe76a3d764aa879af372f4240de8' + '347937e5a90cec9f41ff2f26b8da9a94a225d1a913717d73f10397d' + '2183f1ba3b7b45a68f1ff1893caf69a827802f7b6a48d51da6fbefb' + '64fd9a6c5b75c4561', 16), + 'digest_algorithm': 'SHA-256', + 'msg': binascii.unhexlify( + b'a733b3f588d5ac9b9d4fe2f804df8c256403a9f8eef6f191fc48e1267fb' + b'5b4d546ba11e77b667844e489bf0d5f72990aeb061d01ccd7949a23def7' + b'4a803b7d92d51abfadeb4885ffd8ffd58ab87548a15c087a39b8993b2fa' + b'64c9d31a594eeb7512da16955834336a234435c5a9d0dd9b15a94e11615' + b'4dea63fdc8dd7a512181'), + 'y': int('356ed47537fbf02cb30a8cee0537f300dff1d0c467399ce70b87a87' + '58d5ec9dd256246fccaeb9dfe109f2a984f2ddaa87aad54ce0d31f9' + '07e504521baf4207d7073b0a4a9fc67d8ddda99f87aed6e0367cec2' + '7f9c608af743bf1ee6e11d55a182d43b024ace534029b866f642282' + '8bb81a39aae9601ee81c7f81dd358e69f4e2edfa4654d8a65bc6431' + '1dc86aac4abc1fc7a3f65159661a0d8e288eb8d665cb0adf5ac3d6b' + 'a8e9453facf7542393ae24fd50451d3828086558f7ec528e284935a' + '53f67a1aa8e25d8ad5c4ad55d83aef883a4d9eeb6297e6a53f65049' + 'ba9e2c6b7953a760bc1dc46f78ceaaa2c02f5375dd82e708744aa40' + 'b15799eb81d7e5b1a', 16), + 'r': int('bcd490568c0a89ba311bef88ea4f4b03d273e793722722327095a37' + '8dd6f3522', 16), + 's': int('74498fc43091fcdd2d1ef0775f8286945a01cd72b805256b0451f9c' + 'bd943cf82', 16)} + ] + assert expected == load_fips_dsa_sig_vectors(vector_data) def test_load_fips_ecdsa_key_pair_vectors(): @@ -2145,3 +2604,44 @@ bdcf3035f6829ede041b745955d219dc5d30ddd8b37f6ba0f6d2857504cdc68a1ed812a10 } ] assert expected == load_fips_ecdsa_signing_vectors(vector_data) + + +def test_vector_version(): + assert cryptography.__version__ == cryptography_vectors.__version__ + + +def test_raises_unsupported_algorithm_wrong_type(): + # Check that it raises if the wrong type of exception is raised. + class TestException(Exception): + pass + + with pytest.raises(TestException): + with raises_unsupported_algorithm(None): + raise TestException + + +def test_raises_unsupported_algorithm_wrong_reason(): + # Check that it fails if the wrong reason code is raised. + with pytest.raises(AssertionError): + with raises_unsupported_algorithm(None): + raise UnsupportedAlgorithm("An error.", + _Reasons.BACKEND_MISSING_INTERFACE) + + +def test_raises_unsupported_no_exc(): + # Check that it fails if no exception is raised. + with pytest.raises(pytest.fail.Exception): + with raises_unsupported_algorithm( + _Reasons.BACKEND_MISSING_INTERFACE + ): + pass + + +def test_raises_unsupported_algorithm(): + # Check that it doesn't assert if the right things are raised. + with raises_unsupported_algorithm( + _Reasons.BACKEND_MISSING_INTERFACE + ) as exc_info: + raise UnsupportedAlgorithm("An error.", + _Reasons.BACKEND_MISSING_INTERFACE) + assert exc_info.type is UnsupportedAlgorithm diff --git a/tests/utils.py b/tests/utils.py index c38ba7ff..60b6f5a2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -15,8 +15,8 @@ from __future__ import absolute_import, division, print_function import binascii import collections -from contextlib import contextmanager import re +from contextlib import contextmanager import pytest @@ -431,6 +431,73 @@ def load_fips_dsa_key_pair_vectors(vector_data): return vectors +def load_fips_dsa_sig_vectors(vector_data): + """ + Loads data out of the FIPS DSA SigVer vector files. + """ + vectors = [] + sha_regex = re.compile( + r"\[mod = L=...., N=..., SHA-(?P<sha>1|224|256|384|512)\]" + ) + # When reading_key_data is set to True it tells the loader to continue + # constructing dictionaries. We set reading_key_data to False during the + # blocks of the vectors of N=224 because we don't support it. + reading_key_data = True + + for line in vector_data: + line = line.strip() + + if not line or line.startswith("#"): + continue + + sha_match = sha_regex.match(line) + if sha_match: + digest_algorithm = "SHA-{0}".format(sha_match.group("sha")) + + if line.startswith("[mod = L=2048, N=224"): + reading_key_data = False + continue + elif line.startswith("[mod = L=2048, N=256"): + reading_key_data = True + continue + + if not reading_key_data or line.startswith("[mod"): + continue + + name, value = [c.strip() for c in line.split("=")] + + if name == "P": + vectors.append({'p': int(value, 16), + 'digest_algorithm': digest_algorithm}) + elif name == "Q": + vectors[-1]['q'] = int(value, 16) + elif name == "G": + vectors[-1]['g'] = int(value, 16) + elif name == "Msg" and 'msg' not in vectors[-1]: + hexmsg = value.strip().encode("ascii") + vectors[-1]['msg'] = binascii.unhexlify(hexmsg) + elif name == "Msg" and 'msg' in vectors[-1]: + hexmsg = value.strip().encode("ascii") + vectors.append({'p': vectors[-1]['p'], + 'q': vectors[-1]['q'], + 'g': vectors[-1]['g'], + 'digest_algorithm': + vectors[-1]['digest_algorithm'], + 'msg': binascii.unhexlify(hexmsg)}) + elif name == "X": + vectors[-1]['x'] = int(value, 16) + elif name == "Y": + vectors[-1]['y'] = int(value, 16) + elif name == "R": + vectors[-1]['r'] = int(value, 16) + elif name == "S": + vectors[-1]['s'] = int(value, 16) + elif name == "Result": + vectors[-1]['result'] = value.split("(")[0].strip() + + return vectors + + # http://tools.ietf.org/html/rfc4492#appendix-A _ECDSA_CURVE_NAMES = { "P-192": "secp192r1", @@ -17,8 +17,8 @@ commands = deps = pyenchant sphinx - sphinxcontrib-spelling sphinx_rtd_theme + sphinxcontrib-spelling basepython = python2.7 commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html @@ -42,6 +42,7 @@ commands = [testenv:pep8] deps = flake8 + flake8-import-order pep8-naming commands = flake8 . @@ -49,9 +50,11 @@ commands = flake8 . basepython = python3.3 deps = flake8 + flake8-import-order pep8-naming commands = flake8 . [flake8] exclude = .tox,*.egg -select = E,W,F,N +select = E,W,F,N,I +application-import-names = cryptography,cryptography_vectors,tests diff --git a/vectors/cryptography_vectors/__init__.py b/vectors/cryptography_vectors/__init__.py index 02d748df..25df6b3a 100644 --- a/vectors/cryptography_vectors/__init__.py +++ b/vectors/cryptography_vectors/__init__.py @@ -16,8 +16,8 @@ from __future__ import absolute_import, division, print_function import os from cryptography_vectors.__about__ import ( - __title__, __summary__, __uri__, __version__, __author__, __email__, - __license__, __copyright__ + __author__, __copyright__, __email__, __license__, __summary__, __title__, + __uri__, __version__ ) diff --git a/vectors/setup.py b/vectors/setup.py index ce01e132..66841def 100644 --- a/vectors/setup.py +++ b/vectors/setup.py @@ -15,7 +15,7 @@ from __future__ import absolute_import, division, print_function import os -from setuptools import setup, find_packages +from setuptools import find_packages, setup base_dir = os.path.dirname(__file__) |