import python-cryptography-3.4.7-5.el9

This commit is contained in:
CentOS Sources 2021-11-02 07:10:42 -04:00 committed by Stepan Oksanichenko
commit e2947ffb71
9 changed files with 1108 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
SOURCES/cryptography-3.4.7-vendor.tar.bz2
SOURCES/cryptography-3.4.7.tar.gz

View File

@ -0,0 +1,2 @@
bbc8da55813a9176b9c9ccf33700903c5f6c107f SOURCES/cryptography-3.4.7-vendor.tar.bz2
fff48841f1f4b756a6093bdcc376a3c513ba5aaf SOURCES/cryptography-3.4.7.tar.gz

View File

@ -0,0 +1,130 @@
From cb1908043d5daa7c5c38945c048c4a2477a46221 Mon Sep 17 00:00:00 2001
From: Paul Kehrer <paul.l.kehrer@gmail.com>
Date: Sun, 28 Feb 2021 16:06:11 -0600
Subject: [PATCH 1/3] fix pkcs12 parse ordering. fixes #5872 (#5879)
* fix pkcs12 parse ordering. fixes #5872
* remove an unneeded print
* simplify the test a bit more
* index
* black
* Update tests/hazmat/primitives/test_pkcs12.py
Co-authored-by: Alex Gaynor <alex.gaynor@gmail.com>
Co-authored-by: Alex Gaynor <alex.gaynor@gmail.com>
---
.../hazmat/backends/openssl/backend.py | 5 +-
tests/hazmat/primitives/test_pkcs12.py | 58 ++++++++++++++++++-
2 files changed, 59 insertions(+), 4 deletions(-)
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 271873d9..a96d08d8 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -6,6 +6,7 @@
import collections
import contextlib
import itertools
+import typing
import warnings
from contextlib import contextmanager
@@ -2562,9 +2563,7 @@ class Backend(object):
sk_x509 = self._lib.sk_X509_new_null()
sk_x509 = self._ffi.gc(sk_x509, self._lib.sk_X509_free)
- # reverse the list when building the stack so that they're encoded
- # in the order they were originally provided. it is a mystery
- for ca in reversed(cas):
+ for ca in cas:
res = self._lib.sk_X509_push(sk_x509, ca._x509)
backend.openssl_assert(res >= 1)
diff --git a/tests/hazmat/primitives/test_pkcs12.py b/tests/hazmat/primitives/test_pkcs12.py
index b5de09f9..b1759a1b 100644
--- a/tests/hazmat/primitives/test_pkcs12.py
+++ b/tests/hazmat/primitives/test_pkcs12.py
@@ -4,13 +4,15 @@
import os
+from datetime import datetime
import pytest
from cryptography import x509
from cryptography.hazmat.backends.interfaces import DERSerializationBackend
from cryptography.hazmat.backends.openssl.backend import _RC2
-from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives.serialization.pkcs12 import (
load_key_and_certificates,
@@ -273,3 +275,57 @@ class TestPKCS12Creation(object):
DummyKeySerializationEncryption(),
)
assert str(exc.value) == "Unsupported key encryption type"
+
+
+def test_pkcs12_ordering():
+ """
+ In OpenSSL < 3.0.0 PKCS12 parsing reverses the order. However, we
+ accidentally thought it was **encoding** that did it, leading to bug
+ https://github.com/pyca/cryptography/issues/5872
+ This test ensures our ordering is correct going forward.
+ """
+
+ def make_cert(name):
+ key = ec.generate_private_key(ec.SECP256R1())
+ subject = x509.Name(
+ [
+ x509.NameAttribute(x509.NameOID.COMMON_NAME, name),
+ ]
+ )
+ now = datetime.utcnow()
+ cert = (
+ x509.CertificateBuilder()
+ .subject_name(subject)
+ .issuer_name(subject)
+ .public_key(key.public_key())
+ .serial_number(x509.random_serial_number())
+ .not_valid_before(now)
+ .not_valid_after(now)
+ .sign(key, hashes.SHA256())
+ )
+ return (key, cert)
+
+ # Make some certificates with distinct names.
+ a_name = "A" * 20
+ b_name = "B" * 20
+ c_name = "C" * 20
+ a_key, a_cert = make_cert(a_name)
+ _, b_cert = make_cert(b_name)
+ _, c_cert = make_cert(c_name)
+
+ # Bundle them in a PKCS#12 file in order A, B, C.
+ p12 = serialize_key_and_certificates(
+ b"p12", a_key, a_cert, [b_cert, c_cert], serialization.NoEncryption()
+ )
+
+ # Parse them out. The API should report them in the same order.
+ (key, cert, certs) = load_key_and_certificates(p12, None)
+ assert cert == a_cert
+ assert certs == [b_cert, c_cert]
+
+ # The ordering in the PKCS#12 file itself should also match.
+ a_idx = p12.index(a_name.encode("utf-8"))
+ b_idx = p12.index(b_name.encode("utf-8"))
+ c_idx = p12.index(c_name.encode("utf-8"))
+
+ assert a_idx < b_idx < c_idx
--
2.30.2

View File

@ -0,0 +1,415 @@
From a0bece343e38d73d038d4f3a62c2a9638608ac9c Mon Sep 17 00:00:00 2001
From: Paul Kehrer <paul.l.kehrer@gmail.com>
Date: Thu, 22 Apr 2021 19:16:38 -0500
Subject: [PATCH 2/3] [WIP] 3.0.0 support (#5250)
* 3.0.0 support
* almost...there...
* make mypy happy
---
.github/workflows/ci.yml | 7 ++--
src/_cffi_src/build_openssl.py | 1 +
src/_cffi_src/openssl/cryptography.py | 3 ++
src/_cffi_src/openssl/err.py | 6 +++
src/_cffi_src/openssl/fips.py | 2 +-
src/_cffi_src/openssl/provider.py | 40 ++++++++++++++++++
.../hazmat/backends/openssl/backend.py | 42 ++++++++++++++++---
.../hazmat/backends/openssl/ciphers.py | 15 ++++++-
.../hazmat/bindings/openssl/_conditional.py | 11 +++++
.../hazmat/bindings/openssl/binding.py | 20 +++++++++
tests/hazmat/backends/test_openssl_memleak.py | 6 ++-
tests/hazmat/bindings/test_openssl.py | 4 +-
tests/hazmat/primitives/test_dh.py | 24 ++++++++++-
13 files changed, 167 insertions(+), 14 deletions(-)
create mode 100644 src/_cffi_src/openssl/provider.py
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cd967a3a..747f84c1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,9 +18,10 @@ jobs:
- {VERSION: "3.9", TOXENV: "flake,rust,docs", COVERAGE: "false"}
- {VERSION: "pypy3", TOXENV: "pypy3"}
- {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "1.1.0l"}}
- - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1i"}}
- - {VERSION: "3.9", TOXENV: "py39-ssh", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1i"}}
- - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1i", CONFIG_FLAGS: "no-engine no-rc2 no-srtp no-ct"}}
+ - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1j"}}
+ - {VERSION: "3.9", TOXENV: "py39-ssh", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1j"}}
+ - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "1.1.1j", CONFIG_FLAGS: "no-engine no-rc2 no-srtp no-ct"}}
+ - {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "openssl", VERSION: "3.0.0-alpha15"}}
- {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "libressl", VERSION: "2.9.2"}}
- {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "libressl", VERSION: "3.0.2"}}
- {VERSION: "3.9", TOXENV: "py39", OPENSSL: {TYPE: "libressl", VERSION: "3.1.5"}}
diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py
index 08499d66..557296ed 100644
--- a/src/_cffi_src/build_openssl.py
+++ b/src/_cffi_src/build_openssl.py
@@ -104,6 +104,7 @@ ffi = build_ffi_for_binding(
"osrandom_engine",
"pem",
"pkcs12",
+ "provider",
"rand",
"rsa",
"ssl",
diff --git a/src/_cffi_src/openssl/cryptography.py b/src/_cffi_src/openssl/cryptography.py
index e2b5a132..06d1e778 100644
--- a/src/_cffi_src/openssl/cryptography.py
+++ b/src/_cffi_src/openssl/cryptography.py
@@ -34,6 +34,8 @@ INCLUDES = """
#define CRYPTOGRAPHY_OPENSSL_110F_OR_GREATER \
(OPENSSL_VERSION_NUMBER >= 0x1010006f && !CRYPTOGRAPHY_IS_LIBRESSL)
+#define CRYPTOGRAPHY_OPENSSL_300_OR_GREATER \
+ (OPENSSL_VERSION_NUMBER >= 0x30000000 && !CRYPTOGRAPHY_IS_LIBRESSL)
#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_110J \
(OPENSSL_VERSION_NUMBER < 0x101000af || CRYPTOGRAPHY_IS_LIBRESSL)
@@ -53,6 +55,7 @@ INCLUDES = """
TYPES = """
static const int CRYPTOGRAPHY_OPENSSL_110F_OR_GREATER;
+static const int CRYPTOGRAPHY_OPENSSL_300_OR_GREATER;
static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_111;
static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B;
diff --git a/src/_cffi_src/openssl/err.py b/src/_cffi_src/openssl/err.py
index 0634b656..8cfeaf5b 100644
--- a/src/_cffi_src/openssl/err.py
+++ b/src/_cffi_src/openssl/err.py
@@ -18,6 +18,7 @@ static const int EVP_R_UNKNOWN_PBE_ALGORITHM;
static const int ERR_LIB_EVP;
static const int ERR_LIB_PEM;
+static const int ERR_LIB_PROV;
static const int ERR_LIB_ASN1;
static const int ERR_LIB_PKCS12;
@@ -45,4 +46,9 @@ int ERR_GET_REASON(unsigned long);
"""
CUSTOMIZATIONS = """
+/* This define is tied to provider support and is conditionally
+ removed if Cryptography_HAS_PROVIDERS is false */
+#ifndef ERR_LIB_PROV
+#define ERR_LIB_PROV 0
+#endif
"""
diff --git a/src/_cffi_src/openssl/fips.py b/src/_cffi_src/openssl/fips.py
index b9d0d64d..23c10af9 100644
--- a/src/_cffi_src/openssl/fips.py
+++ b/src/_cffi_src/openssl/fips.py
@@ -17,7 +17,7 @@ int FIPS_mode(void);
"""
CUSTOMIZATIONS = """
-#if CRYPTOGRAPHY_IS_LIBRESSL
+#if CRYPTOGRAPHY_IS_LIBRESSL || CRYPTOGRAPHY_OPENSSL_300_OR_GREATER
static const long Cryptography_HAS_FIPS = 0;
int (*FIPS_mode_set)(int) = NULL;
int (*FIPS_mode)(void) = NULL;
diff --git a/src/_cffi_src/openssl/provider.py b/src/_cffi_src/openssl/provider.py
new file mode 100644
index 00000000..d7d659ea
--- /dev/null
+++ b/src/_cffi_src/openssl/provider.py
@@ -0,0 +1,40 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+
+INCLUDES = """
+#if CRYPTOGRAPHY_OPENSSL_300_OR_GREATER
+#include <openssl/provider.h>
+#include <openssl/proverr.h>
+#endif
+"""
+
+TYPES = """
+static const long Cryptography_HAS_PROVIDERS;
+
+typedef ... OSSL_PROVIDER;
+typedef ... OSSL_LIB_CTX;
+
+static const long PROV_R_BAD_DECRYPT;
+static const long PROV_R_WRONG_FINAL_BLOCK_LENGTH;
+"""
+
+FUNCTIONS = """
+OSSL_PROVIDER *OSSL_PROVIDER_load(OSSL_LIB_CTX *, const char *);
+int OSSL_PROVIDER_unload(OSSL_PROVIDER *prov);
+"""
+
+CUSTOMIZATIONS = """
+#if CRYPTOGRAPHY_OPENSSL_300_OR_GREATER
+static const long Cryptography_HAS_PROVIDERS = 1;
+#else
+static const long Cryptography_HAS_PROVIDERS = 0;
+typedef void OSSL_PROVIDER;
+typedef void OSSL_LIB_CTX;
+static const long PROV_R_BAD_DECRYPT = 0;
+static const long PROV_R_WRONG_FINAL_BLOCK_LENGTH = 0;
+OSSL_PROVIDER *(*OSSL_PROVIDER_load)(OSSL_LIB_CTX *, const char *) = NULL;
+int (*OSSL_PROVIDER_unload)(OSSL_PROVIDER *) = NULL;
+#endif
+"""
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index a96d08d8..86e8f0a8 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -1281,6 +1281,11 @@ class Backend(object):
def _evp_pkey_from_der_traditional_key(self, bio_data, password):
key = self._lib.d2i_PrivateKey_bio(bio_data.bio, self._ffi.NULL)
if key != self._ffi.NULL:
+ # In OpenSSL 3.0.0-alpha15 there exist scenarios where the key will
+ # successfully load but errors are still put on the stack. Tracked
+ # as https://github.com/openssl/openssl/issues/14996
+ self._consume_errors()
+
key = self._ffi.gc(key, self._lib.EVP_PKEY_free)
if password is not None:
raise TypeError(
@@ -1448,6 +1453,11 @@ class Backend(object):
else:
self._handle_key_loading_error()
+ # In OpenSSL 3.0.0-alpha15 there exist scenarios where the key will
+ # successfully load but errors are still put on the stack. Tracked
+ # as https://github.com/openssl/openssl/issues/14996
+ self._consume_errors()
+
evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
if password is not None and userdata.called == 0:
@@ -1470,11 +1480,22 @@ class Backend(object):
"incorrect format or it may be encrypted with an unsupported "
"algorithm."
)
- elif errors[0]._lib_reason_match(
- self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT
- ) or errors[0]._lib_reason_match(
- self._lib.ERR_LIB_PKCS12,
- self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR,
+
+ elif (
+ errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT
+ )
+ or errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_PKCS12,
+ self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR,
+ )
+ or (
+ self._lib.Cryptography_HAS_PROVIDERS
+ and errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_PROV,
+ self._lib.PROV_R_BAD_DECRYPT,
+ )
+ )
):
raise ValueError("Bad decrypt. Incorrect password?")
@@ -2520,7 +2541,16 @@ class Backend(object):
if sk_x509_ptr[0] != self._ffi.NULL:
sk_x509 = self._ffi.gc(sk_x509_ptr[0], self._lib.sk_X509_free)
num = self._lib.sk_X509_num(sk_x509_ptr[0])
- for i in range(num):
+
+ # In OpenSSL < 3.0.0 PKCS12 parsing reverses the order of the
+ # certificates.
+ indices: typing.Iterable[int]
+ if self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
+ indices = range(num)
+ else:
+ indices = reversed(range(num))
+
+ for i in indices:
x509 = self._lib.sk_X509_value(sk_x509, i)
self.openssl_assert(x509 != self._ffi.NULL)
x509 = self._ffi.gc(x509, self._lib.X509_free)
diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py
index 0f96795f..a2dd6894 100644
--- a/src/cryptography/hazmat/backends/openssl/ciphers.py
+++ b/src/cryptography/hazmat/backends/openssl/ciphers.py
@@ -145,7 +145,13 @@ class _CipherContext(object):
res = self._backend._lib.EVP_CipherUpdate(
self._ctx, outbuf, outlen, inbuf, inlen
)
- self._backend.openssl_assert(res != 0)
+ if res == 0 and isinstance(self._mode, modes.XTS):
+ raise ValueError(
+ "In XTS mode you must supply at least a full block in the "
+ "first update call. For AES this is 16 bytes."
+ )
+ else:
+ self._backend.openssl_assert(res != 0)
data_processed += inlen
total_out += outlen[0]
@@ -174,6 +180,13 @@ class _CipherContext(object):
errors[0]._lib_reason_match(
self._backend._lib.ERR_LIB_EVP,
self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,
+ )
+ or (
+ self._backend._lib.Cryptography_HAS_PROVIDERS
+ and errors[0]._lib_reason_match(
+ self._backend._lib.ERR_LIB_PROV,
+ self._backend._lib.PROV_R_WRONG_FINAL_BLOCK_LENGTH,
+ )
),
errors=errors,
)
diff --git a/src/cryptography/hazmat/bindings/openssl/_conditional.py b/src/cryptography/hazmat/bindings/openssl/_conditional.py
index 86548357..1f42c7be 100644
--- a/src/cryptography/hazmat/bindings/openssl/_conditional.py
+++ b/src/cryptography/hazmat/bindings/openssl/_conditional.py
@@ -270,6 +270,16 @@ def cryptography_has_get_proto_version():
]
+def cryptography_has_providers():
+ return [
+ "OSSL_PROVIDER_load",
+ "OSSL_PROVIDER_unload",
+ "ERR_LIB_PROV",
+ "PROV_R_WRONG_FINAL_BLOCK_LENGTH",
+ "PROV_R_BAD_DECRYPT",
+ ]
+
+
# This is a mapping of
# {condition: function-returning-names-dependent-on-that-condition} so we can
# loop over them and delete unsupported names at runtime. It will be removed
@@ -318,4 +328,5 @@ CONDITIONAL_NAMES = {
"Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain,
"Cryptography_HAS_SRTP": cryptography_has_srtp,
"Cryptography_HAS_GET_PROTO_VERSION": cryptography_has_get_proto_version,
+ "Cryptography_HAS_PROVIDERS": cryptography_has_providers,
}
diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py
index a2bc36a8..6dcec26a 100644
--- a/src/cryptography/hazmat/bindings/openssl/binding.py
+++ b/src/cryptography/hazmat/bindings/openssl/binding.py
@@ -113,6 +113,8 @@ class Binding(object):
ffi = ffi
_lib_loaded = False
_init_lock = threading.Lock()
+ _legacy_provider: typing.Any = None
+ _default_provider: typing.Any = None
def __init__(self):
self._ensure_ffi_initialized()
@@ -140,6 +142,24 @@ class Binding(object):
# adds all ciphers/digests for EVP
cls.lib.OpenSSL_add_all_algorithms()
cls._register_osrandom_engine()
+ # As of OpenSSL 3.0.0 we must register a legacy cipher provider
+ # to get RC2 (needed for junk asymmetric private key
+ # serialization), RC4, Blowfish, IDEA, SEED, etc. These things
+ # are ugly legacy, but we aren't going to get rid of them
+ # any time soon.
+ if cls.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
+ cls._legacy_provider = cls.lib.OSSL_PROVIDER_load(
+ cls.ffi.NULL, b"legacy"
+ )
+ _openssl_assert(
+ cls.lib, cls._legacy_provider != cls.ffi.NULL
+ )
+ cls._default_provider = cls.lib.OSSL_PROVIDER_load(
+ cls.ffi.NULL, b"default"
+ )
+ _openssl_assert(
+ cls.lib, cls._default_provider != cls.ffi.NULL
+ )
@classmethod
def init_static_locks(cls):
diff --git a/tests/hazmat/backends/test_openssl_memleak.py b/tests/hazmat/backends/test_openssl_memleak.py
index 0c96516f..0316b5d9 100644
--- a/tests/hazmat/backends/test_openssl_memleak.py
+++ b/tests/hazmat/backends/test_openssl_memleak.py
@@ -82,7 +82,7 @@ def main(argv):
assert result == 1
# Trigger a bunch of initialization stuff.
- import cryptography.hazmat.backends.openssl
+ from cryptography.hazmat.backends.openssl.backend import backend
start_heap = set(heap)
@@ -91,6 +91,10 @@ def main(argv):
gc.collect()
gc.collect()
+ if lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
+ lib.OSSL_PROVIDER_unload(backend._binding._legacy_provider)
+ lib.OSSL_PROVIDER_unload(backend._binding._default_provider)
+
if lib.Cryptography_HAS_OPENSSL_CLEANUP:
lib.OPENSSL_cleanup()
diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py
index fb9a1e36..4d1e3b55 100644
--- a/tests/hazmat/bindings/test_openssl.py
+++ b/tests/hazmat/bindings/test_openssl.py
@@ -91,7 +91,9 @@ class TestOpenSSL(object):
_openssl_assert(b.lib, False)
error = exc_info.value.err_code[0]
- assert error.code == 101183626
+ # As of 3.0.0 OpenSSL sets func codes to 0, so the combined
+ # code is a different value
+ assert error.code in (101183626, 50331786)
assert error.lib == b.lib.ERR_LIB_EVP
assert error.func == b.lib.EVP_F_EVP_ENCRYPTFINAL_EX
assert error.reason == b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py
index 131807fc..bb29919f 100644
--- a/tests/hazmat/primitives/test_dh.py
+++ b/tests/hazmat/primitives/test_dh.py
@@ -180,7 +180,23 @@ class TestDH(object):
params = dh.DHParameterNumbers(p, int(vector["g"]))
param = params.parameters(backend)
key = param.generate_private_key()
- assert key.private_numbers().public_numbers.parameter_numbers == params
+ # In OpenSSL 3.0.0 OpenSSL maps to known groups. This results in
+ # a scenario where loading a known group with p and g returns a
+ # re-serialized form that has q as well (the Sophie Germain prime of
+ # that group). This makes a naive comparison of the parameter numbers
+ # objects fail, so we have to be a bit smarter
+ serialized_params = (
+ key.private_numbers().public_numbers.parameter_numbers
+ )
+ if serialized_params.q is None:
+ # This is the path OpenSSL < 3.0 takes
+ assert serialized_params == params
+ else:
+ assert serialized_params.p == params.p
+ assert serialized_params.g == params.g
+ # p = 2q + 1 since it is a Sophie Germain prime, so we can compute
+ # what we expect OpenSSL to have done here.
+ assert serialized_params.q == (params.p - 1) // 2
@pytest.mark.skip_fips(reason="non-FIPS parameters")
@pytest.mark.parametrize(
@@ -382,6 +398,12 @@ class TestDH(object):
assert symkey1 != symkey2
@pytest.mark.skip_fips(reason="key_size too small for FIPS")
+ @pytest.mark.supported(
+ only_if=lambda backend: (
+ not backend._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER
+ ),
+ skip_message="256-bit DH keys are not supported in OpenSSL 3.0.0+",
+ )
def test_load_256bit_key_from_pkcs8(self, backend):
data = load_vectors_from_file(
os.path.join("asymmetric", "DH", "dh_key_256.pem"),
--
2.30.2

View File

@ -0,0 +1,151 @@
From 29cf9b8d63ef3437ba11aa29502af8773faa17a7 Mon Sep 17 00:00:00 2001
From: Paul Kehrer <paul.l.kehrer@gmail.com>
Date: Wed, 14 Apr 2021 13:15:57 -0500
Subject: [PATCH 3/3] switch to using EVP_PKEY_derive instead of DH_compute_key
in DH (#5972)
* switch to using EVP_PKEY_derive instead of DH_compute_key in DH
Where checks are occurring is changing in OpenSSL 3.0 and this makes it
easier to be consistent (and is the API we should be using anyway). The
tests change because EVP_PKEY_derive now verifies that we have shared
parameters, which the test previously only verified by asserting that
the derived keys didn't match
* review feedback
* type ignores required for typeerror tests. some day i will remember this
---
src/_cffi_src/openssl/dh.py | 1 -
.../hazmat/backends/openssl/dh.py | 57 ++++++++++++-------
tests/hazmat/primitives/test_dh.py | 19 ++++---
3 files changed, 45 insertions(+), 32 deletions(-)
diff --git a/src/_cffi_src/openssl/dh.py b/src/_cffi_src/openssl/dh.py
index 979dafa9..50989e45 100644
--- a/src/_cffi_src/openssl/dh.py
+++ b/src/_cffi_src/openssl/dh.py
@@ -18,7 +18,6 @@ DH *DH_new(void);
void DH_free(DH *);
int DH_size(const DH *);
int DH_generate_key(DH *);
-int DH_compute_key(unsigned char *, const BIGNUM *, DH *);
DH *DHparams_dup(DH *);
/* added in 1.1.0 when the DH struct was opaqued */
diff --git a/src/cryptography/hazmat/backends/openssl/dh.py b/src/cryptography/hazmat/backends/openssl/dh.py
index 65ddaeec..b928f024 100644
--- a/src/cryptography/hazmat/backends/openssl/dh.py
+++ b/src/cryptography/hazmat/backends/openssl/dh.py
@@ -127,35 +127,48 @@ class _DHPrivateKey(dh.DHPrivateKey):
)
def exchange(self, peer_public_key: dh.DHPublicKey) -> bytes:
- buf = self._backend._ffi.new("unsigned char[]", self._key_size_bytes)
- pub_key = self._backend._ffi.new("BIGNUM **")
- self._backend._lib.DH_get0_key(
- peer_public_key._dh_cdata, # type: ignore[attr-defined]
- pub_key,
- self._backend._ffi.NULL,
+ if not isinstance(peer_public_key, _DHPublicKey):
+ raise TypeError("peer_public_key must be a DHPublicKey")
+
+ ctx = self._backend._lib.EVP_PKEY_CTX_new(
+ self._evp_pkey, self._backend._ffi.NULL
)
- self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
- res = self._backend._lib.DH_compute_key(
- buf, pub_key[0], self._dh_cdata
+ self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
+ ctx = self._backend._ffi.gc(ctx, self._backend._lib.EVP_PKEY_CTX_free)
+ res = self._backend._lib.EVP_PKEY_derive_init(ctx)
+ self._backend.openssl_assert(res == 1)
+ res = self._backend._lib.EVP_PKEY_derive_set_peer(
+ ctx, peer_public_key._evp_pkey
+ )
+ # Invalid kex errors here in OpenSSL 3.0 because checks were moved
+ # to EVP_PKEY_derive_set_peer
+ self._exchange_assert(res == 1)
+ keylen = self._backend._ffi.new("size_t *")
+ res = self._backend._lib.EVP_PKEY_derive(
+ ctx, self._backend._ffi.NULL, keylen
)
+ # Invalid kex errors here in OpenSSL < 3
+ self._exchange_assert(res == 1)
+ self._backend.openssl_assert(keylen[0] > 0)
+ buf = self._backend._ffi.new("unsigned char[]", keylen[0])
+ res = self._backend._lib.EVP_PKEY_derive(ctx, buf, keylen)
+ self._backend.openssl_assert(res == 1)
- if res == -1:
+ key = self._backend._ffi.buffer(buf, keylen[0])[:]
+ pad = self._key_size_bytes - len(key)
+
+ if pad > 0:
+ key = (b"\x00" * pad) + key
+
+ return key
+
+ def _exchange_assert(self, ok):
+ if not ok:
errors_with_text = self._backend._consume_errors_with_text()
raise ValueError(
- "Error computing shared key. Public key is likely invalid "
- "for this exchange.",
+ "Error computing shared key.",
errors_with_text,
)
- else:
- self._backend.openssl_assert(res >= 1)
-
- key = self._backend._ffi.buffer(buf)[:res]
- pad = self._key_size_bytes - len(key)
-
- if pad > 0:
- key = (b"\x00" * pad) + key
-
- return key
def public_key(self) -> dh.DHPublicKey:
dh_cdata = _dh_params_dup(self._dh_cdata, self._backend)
diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py
index bb29919f..2914f7e7 100644
--- a/tests/hazmat/primitives/test_dh.py
+++ b/tests/hazmat/primitives/test_dh.py
@@ -296,6 +296,12 @@ class TestDH(object):
assert isinstance(key.private_numbers(), dh.DHPrivateNumbers)
assert isinstance(key.parameters(), dh.DHParameters)
+ def test_exchange_wrong_type(self, backend):
+ parameters = FFDH3072_P.parameters(backend)
+ key1 = parameters.generate_private_key()
+ with pytest.raises(TypeError):
+ key1.exchange(b"invalidtype") # type: ignore[arg-type]
+
def test_exchange(self, backend):
parameters = FFDH3072_P.parameters(backend)
assert isinstance(parameters, dh.DHParameters)
@@ -386,16 +392,11 @@ class TestDH(object):
key2 = private2.private_key(backend)
pub_key2 = key2.public_key()
- if pub_key2.public_numbers().y >= parameters1.p:
- with pytest.raises(ValueError):
- key1.exchange(pub_key2)
- else:
- symkey1 = key1.exchange(pub_key2)
- assert symkey1
-
- symkey2 = key2.exchange(pub_key1)
+ with pytest.raises(ValueError):
+ key1.exchange(pub_key2)
- assert symkey1 != symkey2
+ with pytest.raises(ValueError):
+ key2.exchange(pub_key1)
@pytest.mark.skip_fips(reason="key_size too small for FIPS")
@pytest.mark.supported(
--
2.30.2

View File

@ -0,0 +1,84 @@
From 417984a48106626b5f6a62763113c0ed1f741f5b Mon Sep 17 00:00:00 2001
From: Paul Kehrer <paul.l.kehrer@gmail.com>
Date: Wed, 30 Jun 2021 06:14:42 -0500
Subject: [PATCH 4/5] 3.0.0 deprecated func and it isn't useful to us in
general (#6148)
remove it everywhere and assert on the code/lib/reason
---
src/cryptography/hazmat/bindings/openssl/binding.py | 11 ++++-------
tests/hazmat/bindings/test_openssl.py | 5 ++---
2 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py
index 6dcec26a..f651ab67 100644
--- a/src/cryptography/hazmat/bindings/openssl/binding.py
+++ b/src/cryptography/hazmat/bindings/openssl/binding.py
@@ -15,15 +15,14 @@ from cryptography.hazmat.bindings._openssl import ffi, lib
from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES
_OpenSSLErrorWithText = collections.namedtuple(
- "_OpenSSLErrorWithText", ["code", "lib", "func", "reason", "reason_text"]
+ "_OpenSSLErrorWithText", ["code", "lib", "reason", "reason_text"]
)
class _OpenSSLError(object):
- def __init__(self, code, lib, func, reason):
+ def __init__(self, code, lib, reason):
self._code = code
self._lib = lib
- self._func = func
self._reason = reason
def _lib_reason_match(self, lib, reason):
@@ -31,7 +30,6 @@ class _OpenSSLError(object):
code = utils.read_only_property("_code")
lib = utils.read_only_property("_lib")
- func = utils.read_only_property("_func")
reason = utils.read_only_property("_reason")
@@ -43,10 +41,9 @@ def _consume_errors(lib):
break
err_lib = lib.ERR_GET_LIB(code)
- err_func = lib.ERR_GET_FUNC(code)
err_reason = lib.ERR_GET_REASON(code)
- errors.append(_OpenSSLError(code, err_lib, err_func, err_reason))
+ errors.append(_OpenSSLError(code, err_lib, err_reason))
return errors
@@ -60,7 +57,7 @@ def _errors_with_text(errors):
errors_with_text.append(
_OpenSSLErrorWithText(
- err.code, err.lib, err.func, err.reason, err_text_reason
+ err.code, err.lib, err.reason, err_text_reason
)
)
diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py
index 4d1e3b55..1d9b87ba 100644
--- a/tests/hazmat/bindings/test_openssl.py
+++ b/tests/hazmat/bindings/test_openssl.py
@@ -91,11 +91,10 @@ class TestOpenSSL(object):
_openssl_assert(b.lib, False)
error = exc_info.value.err_code[0]
- # As of 3.0.0 OpenSSL sets func codes to 0, so the combined
- # code is a different value
+ # As of 3.0.0 OpenSSL no longer sets func codes (which we now also
+ # ignore), so the combined code is a different value
assert error.code in (101183626, 50331786)
assert error.lib == b.lib.ERR_LIB_EVP
- assert error.func == b.lib.EVP_F_EVP_ENCRYPTFINAL_EX
assert error.reason == b.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
assert b"data not multiple of block length" in error.reason_text
--
2.31.1

View File

@ -0,0 +1,24 @@
From 4df2a650e2f8be0dc118128d39aebe6f32d06d43 Mon Sep 17 00:00:00 2001
From: Paul Kehrer <paul.l.kehrer@gmail.com>
Date: Wed, 30 Jun 2021 21:12:46 -0500
Subject: [PATCH 5/5] remove unneeded binding (#6150)
---
src/_cffi_src/openssl/err.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/_cffi_src/openssl/err.py b/src/_cffi_src/openssl/err.py
index 8cfeaf5b..8d838d4f 100644
--- a/src/_cffi_src/openssl/err.py
+++ b/src/_cffi_src/openssl/err.py
@@ -40,7 +40,6 @@ void ERR_clear_error(void);
void ERR_put_error(int, int, int, const char *, int);
int ERR_GET_LIB(unsigned long);
-int ERR_GET_FUNC(unsigned long);
int ERR_GET_REASON(unsigned long);
"""
--
2.31.1

View File

@ -0,0 +1,22 @@
class Skipper:
"""Skip iso8601 and pretend tests
RHEL buildroot doesn't have python-iso8601 and python-pretend. Skip
all tests that use the excluded modules.
"""
def parse_date(self, datestring):
pytest.skip(f"iso8601 module is not available.")
def stub(self, **kwargs):
pytest.skip(f"pretend module is not available.")
def raiser(self, exc):
pytest.skip(f"pretend module is not available.")
import sys
sys.modules["iso8601"] = sys.modules["pretend"] = Skipper()

View File

@ -0,0 +1,278 @@
%bcond_without tests
%{!?python3_pkgversion:%global python3_pkgversion 3}
%global srcname cryptography
%global pyo3_version 0.13.1
Name: python-%{srcname}
Version: 3.4.7
Release: 5%{?dist}
Summary: PyCA's cryptography library
License: ASL 2.0 or BSD
URL: https://cryptography.io/en/latest/
Source0: https://github.com/pyca/cryptography/archive/%{version}/%{srcname}-%{version}.tar.gz
# created by ./vendor_rust.py helper script
Source1: cryptography-%{version}-vendor.tar.bz2
Source2: conftest-skipper.py
# OpenSSL 3.0.0 patches
Patch1: 0001-fix-pkcs12-parse-ordering.-fixes-5872-5879.patch
Patch2: 0002-WIP-3.0.0-support-5250.patch
Patch3: 0003-switch-to-using-EVP_PKEY_derive-instead-of-DH_comput.patch
Patch4: 0004-3.0.0-deprecated-func-and-it-isn-t-useful-to-us-in-g.patch
Patch5: 0005-remove-unneeded-binding-6150.patch
ExclusiveArch: %{rust_arches}
BuildRequires: openssl-devel
BuildRequires: gcc
BuildRequires: gnupg2
%if 0%{?fedora}
BuildRequires: rust-packaging
%else
BuildRequires: rust-toolset
%endif
BuildRequires: python%{python3_pkgversion}-cffi >= 1.7
BuildRequires: python%{python3_pkgversion}-devel
BuildRequires: python%{python3_pkgversion}-setuptools
BuildRequires: python%{python3_pkgversion}-setuptools-rust >= 0.11.3
BuildRequires: python%{python3_pkgversion}-six >= 1.4.1
%if %{with tests}
%if 0%{?fedora}
BuildRequires: python%{python3_pkgversion}-hypothesis >= 1.11.4
BuildRequires: python%{python3_pkgversion}-iso8601
BuildRequires: python%{python3_pkgversion}-pretend
BuildRequires: python%{python3_pkgversion}-pytest-xdist
%endif
BuildRequires: python%{python3_pkgversion}-pytest >= 6.0
BuildRequires: python%{python3_pkgversion}-pytest-subtests >= 0.3.2
BuildRequires: python%{python3_pkgversion}-pytz
%endif
%description
cryptography is a package designed to expose cryptographic primitives and
recipes to Python developers.
%package -n python%{python3_pkgversion}-%{srcname}
Summary: PyCA's cryptography library
%{?python_provide:%python_provide python%{python3_pkgversion}-%{srcname}}
Requires: openssl-libs
Requires: python%{python3_pkgversion}-six >= 1.4.1
Requires: python%{python3_pkgversion}-cffi >= 1.7
%if 0%{?fedora} >= 35 || 0%{?rhel} >= 9
# Can be safely removed in Fedora 37
Obsoletes: python%{python3_pkgversion}-cryptography-vectors < 3.4.7
%endif
%description -n python%{python3_pkgversion}-%{srcname}
cryptography is a package designed to expose cryptographic primitives and
recipes to Python developers.
%prep
%autosetup -p1 -n %{srcname}-%{version}
%generate_buildrequires
%if 0%{?fedora}
# Fedora: use cargo macros to make use of RPMified crates
%cargo_prep
cd src/rust
rm -f Cargo.lock
%cargo_generate_buildrequires
cd ../..
%else
# RHEL: use vendored Rust crates
%cargo_prep -V 1
%endif
%build
%py3_build
%install
# Actually other *.c and *.h are appropriate
# see https://github.com/pyca/cryptography/issues/1463
find . -name .keep -print -delete
%py3_install
%check
%if %{with tests}
%if 0%{?rhel}
# skip hypothesis tests on RHEL
rm -rf tests/hypothesis
# append skipper to skip iso8601 and pretend tests
cat < %{SOURCE2} >> tests/conftest.py
%endif
# see https://github.com/pyca/cryptography/issues/4885 and
# see https://bugzilla.redhat.com/show_bug.cgi?id=1761194 for deselected tests
PYTHONPATH=${PWD}/vectors:%{buildroot}%{python3_sitearch} \
%{__python3} -m pytest \
-k "not (test_buffer_protocol_alternate_modes or test_dh_parameters_supported or test_load_ecdsa_no_named_curve)"
%endif
%files -n python%{python3_pkgversion}-%{srcname}
%doc README.rst docs
%license LICENSE LICENSE.APACHE LICENSE.BSD
%{python3_sitearch}/%{srcname}
%{python3_sitearch}/%{srcname}-%{version}-py*.egg-info
%changelog
* Tue Aug 10 2021 Mohan Boddu <mboddu@redhat.com> - 3.4.7-5
- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
Related: rhbz#1991688
* Sun Aug 08 2021 Christian Heimes <cheimes@redhat.com> - 3.4.7-4
- Remove bindings to ERR_GET_FUNC, which has been removed in 3.0.0-beta2
- Resolves: rhbz#1953446
* Tue Jun 15 2021 Mohan Boddu <mboddu@redhat.com> - 3.4.7-3
- Rebuilt for RHEL 9 BETA for openssl 3.0
- Related: rhbz#1971065
* Mon Apr 26 2021 Christian Heimes <cheimes@redhat.com> - 3.4.7-2
- Add backports of OpenSSL 3.0.0 fixes (upstream PR #6000)
- Resolves: rhbz#1953446
* Wed Apr 21 2021 Christian Heimes <cheimes@redhat.com> - 3.4.7-1
- Update to 3.4.7
- Remove dependency on python-cryptography-vectors package and use vectors
directly from Github source tar ball. Related: rhbz#1952343
* Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 3.4.6-2
- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
* Wed Mar 03 2021 Christian Heimes <cheimes@redhat.com> - 3.4.6-1
- Update to 3.4.6 (#1927044)
* Mon Feb 15 2021 Christian Heimes <cheimes@redhat.com> - 3.4.5-1
- Update to 3.4.5 (#1927044)
* Fri Feb 12 2021 Christian Heimes <cheimes@redhat.com> - 3.4.4-3
- Skip iso8601 and pretend tests on RHEL
* Fri Feb 12 2021 Christian Heimes <cheimes@redhat.com> - 3.4.4-2
- Provide RHEL build infrastructure
* Wed Feb 10 2021 Christian Heimes <cheimes@redhat.com> - 3.4.4-1
- Update to 3.4.4 (#1927044)
* Mon Feb 08 2021 Christian Heimes <cheimes@redhat.com> - 3.4.2-1
- Update to 3.4.2 (#1926339)
- Package no longer depends on Rust (#1926181)
* Mon Feb 08 2021 Fabio Valentini <decathorpe@gmail.com> - 3.4.1-2
- Use dynamically generated BuildRequires for PyO3 Rust module.
- Drop unnecessary CARGO_NET_OFFLINE environment variable.
* Sun Feb 07 2021 Christian Heimes <cheimes@redhat.com> - 3.4.1-1
- Update to 3.4.1 (#1925953)
* Sun Feb 07 2021 Christian Heimes <cheimes@redhat.com> - 3.4-2
- Add missing abi3 and pytest dependencies
* Sun Feb 07 2021 Christian Heimes <cheimes@redhat.com> - 3.4-1
- Update to 3.4 (#1925953)
- Remove Python 2 support
- Remove unused python-idna dependency
- Add Rust support
* Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 3.3.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
* Thu Dec 10 2020 Christian Heimes <cheimes@redhat.com> - 3.3.1-1
- Update to 3.3.1 (#1905756)
* Wed Oct 28 2020 Christian Heimes <cheimes@redhat.com> - 3.2.1-1
- Update to 3.2.1 (#1892153)
* Mon Oct 26 2020 Christian Heimes <cheimes@redhat.com> - 3.2-1
- Update to 3.2 (#1891378)
* Mon Sep 07 2020 Christian Heimes <cheimes@redhat.com> - 3.1-1
- Update to 3.1 (#1872978)
* Wed Jul 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 3.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
* Tue Jul 21 2020 Christian Heimes <cheimes@redhat.com> - 3.0-1
- Update to 3.0 (#185897)
* Sat May 23 2020 Miro Hrončok <mhroncok@redhat.com> - 2.9-3
- Rebuilt for Python 3.9
* Tue May 12 2020 Felix Schwarz <fschwarz@fedoraproject.org> - 2.9-2
- add source file verification
* Fri Apr 03 2020 Christian Heimes <cheimes@redhat.com> - 2.9-1
- Update to 2.9 (#1820348)
* Thu Jan 30 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.8-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
* Mon Jan 13 2020 Christian Heimes <cheimes@redhat.com> - 2.8-2
- cryptography 2.8+ no longer depends on python-asn1crypto
* Thu Oct 17 2019 Christian Heimes <cheimes@redhat.com> - 2.8-1
- Update to 2.8
- Resolves: rhbz#1762779
* Sun Oct 13 2019 Christian Heimes <cheimes@redhat.com> - 2.7-3
- Skip unit tests that fail with OpenSSL 1.1.1.d
- Resolves: rhbz#1761194
- Fix and simplify Python 3 packaging
* Sat Oct 12 2019 Christian Heimes <cheimes@redhat.com> - 2.7-2
- Drop Python 2 package
- Resolves: rhbz#1761081
* Tue Sep 03 2019 Randy Barlow <bowlofeggs@fedoraproject.org> - 2.7-1
- Update to 2.7 (#1715680).
* Fri Aug 16 2019 Miro Hrončok <mhroncok@redhat.com> - 2.6.1-3
- Rebuilt for Python 3.8
* Fri Jul 26 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.6.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
* Thu Feb 28 2019 Christian Heimes <cheimes@redhat.com> - 2.6.1-1
- New upstream release 2.6.1, resolves RHBZ#1683691
* Wed Feb 13 2019 Alfredo Moralejo <amoralej@redhat.com> - 2.5-1
- Updated to 2.5.
* Sat Feb 02 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.3-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Mon Aug 13 2018 Christian Heimes <cheimes@redhat.com> - 2.3-2
- Use TLSv1.2 in test as workaround for RHBZ#1615143
* Wed Jul 18 2018 Christian Heimes <cheimes@redhat.com> - 2.3-1
- New upstream release 2.3
- Fix AEAD tag truncation bug, RHBZ#1602752
* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 2.2.1-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Fri Jun 15 2018 Miro Hrončok <mhroncok@redhat.com> - 2.2.1-2
- Rebuilt for Python 3.7
* Wed Mar 21 2018 Christian Heimes <cheimes@redhat.com> - 2.2.1-1
- New upstream release 2.2.1
* Sun Feb 18 2018 Christian Heimes <cheimes@redhat.com> - 2.1.4-1
- New upstream release 2.1.4
* Sun Feb 18 2018 Christian Heimes <cheimes@redhat.com> - 2.1.3-4
- Build requires gcc
* Mon Feb 12 2018 Iryna Shcherbina <ishcherb@redhat.com> - 2.1.3-3
- Update Python 2 dependency declarations to new packaging standards
(See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3)
* Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 2.1.3-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild