aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rwxr-xr-x.travis/install.sh2
-rw-r--r--README.rst4
-rw-r--r--docs/development/test-vectors.rst13
-rw-r--r--src/_cffi_src/build_openssl.py1
-rw-r--r--src/_cffi_src/openssl/engine.py11
-rw-r--r--src/_cffi_src/openssl/err.py1
-rw-r--r--src/_cffi_src/openssl/osrandom_engine.py31
-rw-r--r--src/_cffi_src/openssl/src/osrandom_engine.c167
-rw-r--r--src/_cffi_src/openssl/src/osrandom_engine.h6
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py19
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py40
-rw-r--r--src/cryptography/hazmat/bindings/openssl/binding.py49
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/base.py19
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/modes.py2
-rw-r--r--tests/conftest.py8
-rw-r--r--tests/hazmat/backends/test_openssl.py12
-rw-r--r--tests/hazmat/bindings/test_openssl.py34
-rw-r--r--tests/hazmat/primitives/test_aes.py50
-rw-r--r--tests/test_interfaces.py4
-rw-r--r--tests/test_utils.py4
-rw-r--r--tests/test_x509_ext.py82
-rw-r--r--tests/utils.py9
-rw-r--r--vectors/cryptography_vectors/x509/custom/nc_permitted_2.pem18
-rw-r--r--vectors/cryptography_vectors/x509/custom/nc_permitted_excluded_2.pem18
-rw-r--r--vectors/cryptography_vectors/x509/custom/nc_single_ip_netmask.pem19
26 files changed, 379 insertions, 246 deletions
diff --git a/.travis.yml b/.travis.yml
index 76decf62..8ad514fd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -120,7 +120,7 @@ script:
- ./.travis/run.sh
after_success:
- - source ~/.venv/bin/activate && coveralls
+ - source ~/.venv/bin/activate && bash <(curl -s https://codecov.io/bash) -e TRAVIS_OS_NAME,TOXENV,OPENSSL
notifications:
irc:
diff --git a/.travis/install.sh b/.travis/install.sh
index a046a5d8..17aee435 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -65,4 +65,4 @@ fi
python -m virtualenv ~/.venv
source ~/.venv/bin/activate
-pip install tox coveralls
+pip install tox codecov
diff --git a/README.rst b/README.rst
index 98e8d17e..949826e6 100644
--- a/README.rst
+++ b/README.rst
@@ -12,8 +12,8 @@ Cryptography
.. image:: https://travis-ci.org/pyca/cryptography.svg?branch=master
:target: https://travis-ci.org/pyca/cryptography
-.. image:: https://img.shields.io/coveralls/pyca/cryptography/master.svg
- :target: https://coveralls.io/r/pyca/cryptography?branch=master
+.. image:: https://codecov.io/github/pyca/cryptography/coverage.svg?branch=master
+ :target: https://codecov.io/github/pyca/cryptography?branch=master
``cryptography`` is a package which provides cryptographic recipes and
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index ac667bb7..fe64fe1a 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -199,14 +199,25 @@ Custom X.509 Vectors
set.
* ``nc_permitted_excluded.pem`` - An RSA 2048 bit self-signed certificate
containing a name constraints extension with both permitted and excluded
- elements.
+ elements. Contains ``IPv4`` and ``IPv6`` addresses with network mask as well
+ as ``dNSName`` with a leading period.
+* ``nc_permitted_excluded_2.pem`` - An RSA 2048 bit self-signed certificate
+ containing a name constraints extension with both permitted and excluded
+ elements. Unlike ``nc_permitted_excluded.pem``, the general names do not
+ contain any name constraints specific values.
* ``nc_permitted.pem`` - An RSA 2048 bit self-signed certificate containing a
name constraints extension with permitted elements.
+* ``nc_permitted_2.pem`` - An RSA 2048 bit self-signed certificate containing a
+ name constraints extension with permitted elements that do not contain any
+ name constraints specific values.
* ``nc_excluded.pem`` - An RSA 2048 bit self-signed certificate containing a
name constraints extension with excluded elements.
* ``nc_invalid_ip_netmask.pem`` - An RSA 2048 bit self-signed certificate
containing a name constraints extension with a permitted element that has an
``IPv6`` IP and an invalid network mask.
+* ``nc_single_ip_netmask.pem`` - An RSA 2048 bit self-signed certificate
+ containing a name constraints extension with a permitted element that has two
+ IPs with ``/32`` and ``/128`` network masks.
* ``cp_user_notice_with_notice_reference.pem`` - An RSA 2048 bit self-signed
certificate containing a certificate policies extension with a
notice reference in the user notice.
diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py
index 1ebadccb..dac3e4d8 100644
--- a/src/_cffi_src/build_openssl.py
+++ b/src/_cffi_src/build_openssl.py
@@ -78,7 +78,6 @@ ffi = build_ffi_for_binding(
"nid",
"objects",
"opensslv",
- "osrandom_engine",
"pem",
"pkcs7",
"pkcs12",
diff --git a/src/_cffi_src/openssl/engine.py b/src/_cffi_src/openssl/engine.py
index 3ebfa6c1..5079fd69 100644
--- a/src/_cffi_src/openssl/engine.py
+++ b/src/_cffi_src/openssl/engine.py
@@ -17,9 +17,16 @@ typedef ... DSA_METHOD;
typedef ... ECDH_METHOD;
typedef ... ECDSA_METHOD;
typedef ... DH_METHOD;
-typedef ... RAND_METHOD;
+typedef struct {
+ void (*seed)(const void *, int);
+ int (*bytes)(unsigned char *, int);
+ void (*cleanup)();
+ void (*add)(const void *, int, double);
+ int (*pseudorand)(unsigned char *, int);
+ int (*status)();
+} RAND_METHOD;
typedef ... STORE_METHOD;
-typedef ... *ENGINE_GEN_INT_FUNC_PTR;
+typedef int (*ENGINE_GEN_INT_FUNC_PTR)(ENGINE *);
typedef ... *ENGINE_CTRL_FUNC_PTR;
typedef ... *ENGINE_LOAD_KEY_PTR;
typedef ... *ENGINE_CIPHERS_PTR;
diff --git a/src/_cffi_src/openssl/err.py b/src/_cffi_src/openssl/err.py
index 0ee19c9e..eebf19ba 100644
--- a/src/_cffi_src/openssl/err.py
+++ b/src/_cffi_src/openssl/err.py
@@ -251,6 +251,7 @@ unsigned long ERR_peek_error_line(const char **, int *);
unsigned long ERR_peek_last_error_line(const char **, int *);
unsigned long ERR_get_error_line_data(const char **, int *,
const char **, int *);
+void ERR_clear_error(void);
unsigned long ERR_peek_error_line_data(const char **,
int *, const char **, int *);
unsigned long ERR_peek_last_error_line_data(const char **,
diff --git a/src/_cffi_src/openssl/osrandom_engine.py b/src/_cffi_src/openssl/osrandom_engine.py
deleted file mode 100644
index a8479b07..00000000
--- a/src/_cffi_src/openssl/osrandom_engine.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# 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.
-
-from __future__ import absolute_import, division, print_function
-
-import os
-
-with open(os.path.join(
- os.path.dirname(__file__), "src/osrandom_engine.h"
-)) as f:
- INCLUDES = f.read()
-
-TYPES = """
-static const char *const Cryptography_osrandom_engine_name;
-static const char *const Cryptography_osrandom_engine_id;
-"""
-
-FUNCTIONS = """
-int Cryptography_add_osrandom_engine(void);
-"""
-
-MACROS = """
-"""
-
-with open(os.path.join(
- os.path.dirname(__file__), "src/osrandom_engine.c"
-)) as f:
- CUSTOMIZATIONS = f.read()
-
-CONDITIONAL_NAMES = {}
diff --git a/src/_cffi_src/openssl/src/osrandom_engine.c b/src/_cffi_src/openssl/src/osrandom_engine.c
deleted file mode 100644
index 27894712..00000000
--- a/src/_cffi_src/openssl/src/osrandom_engine.c
+++ /dev/null
@@ -1,167 +0,0 @@
-static const char *Cryptography_osrandom_engine_id = "osrandom";
-static const char *Cryptography_osrandom_engine_name = "osrandom_engine";
-
-#if defined(_WIN32)
-static HCRYPTPROV hCryptProv = 0;
-
-static int osrandom_init(ENGINE *e) {
- if (hCryptProv > 0) {
- return 1;
- }
- if (CryptAcquireContext(&hCryptProv, NULL, NULL,
- PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
- return 1;
- } else {
- return 0;
- }
-}
-
-static int osrandom_rand_bytes(unsigned char *buffer, int size) {
- if (hCryptProv == 0) {
- return 0;
- }
-
- if (!CryptGenRandom(hCryptProv, (DWORD)size, buffer)) {
- ERR_put_error(
- ERR_LIB_RAND, 0, ERR_R_RAND_LIB, "osrandom_engine.py", 0
- );
- return 0;
- }
- return 1;
-}
-
-static int osrandom_finish(ENGINE *e) {
- if (CryptReleaseContext(hCryptProv, 0)) {
- hCryptProv = 0;
- return 1;
- } else {
- return 0;
- }
-}
-
-static int osrandom_rand_status(void) {
- if (hCryptProv == 0) {
- return 0;
- } else {
- return 1;
- }
-}
-#else
-static int urandom_fd = -1;
-
-static int osrandom_finish(ENGINE *e);
-
-static int osrandom_init(ENGINE *e) {
- if (urandom_fd > -1) {
- return 1;
- }
- urandom_fd = open("/dev/urandom", O_RDONLY);
- if (urandom_fd > -1) {
- int flags = fcntl(urandom_fd, F_GETFD);
- if (flags == -1) {
- osrandom_finish(e);
- return 0;
- } else if (fcntl(urandom_fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
- osrandom_finish(e);
- return 0;
- }
- return 1;
- } else {
- return 0;
- }
-}
-
-static int osrandom_rand_bytes(unsigned char *buffer, int size) {
- ssize_t n;
- while (size > 0) {
- do {
- n = read(urandom_fd, buffer, (size_t)size);
- } while (n < 0 && errno == EINTR);
- if (n <= 0) {
- ERR_put_error(
- ERR_LIB_RAND, 0, ERR_R_RAND_LIB, "osrandom_engine.py", 0
- );
- return 0;
- }
- buffer += n;
- size -= n;
- }
- return 1;
-}
-
-static int osrandom_finish(ENGINE *e) {
- int n;
- do {
- n = close(urandom_fd);
- } while (n < 0 && errno == EINTR);
- urandom_fd = -1;
- if (n < 0) {
- return 0;
- } else {
- return 1;
- }
-}
-
-static int osrandom_rand_status(void) {
- if (urandom_fd == -1) {
- return 0;
- } else {
- return 1;
- }
-}
-#endif
-
-/* This replicates the behavior of the OpenSSL FIPS RNG, which returns a
- -1 in the event that there is an error when calling RAND_pseudo_bytes. */
-static int osrandom_pseudo_rand_bytes(unsigned char *buffer, int size) {
- int res = osrandom_rand_bytes(buffer, size);
- if (res == 0) {
- return -1;
- } else {
- return res;
- }
-}
-
-static RAND_METHOD osrandom_rand = {
- NULL,
- osrandom_rand_bytes,
- NULL,
- NULL,
- osrandom_pseudo_rand_bytes,
- osrandom_rand_status,
-};
-
-/* Returns 1 if successfully added, 2 if engine has previously been added,
- and 0 for error. */
-int Cryptography_add_osrandom_engine(void) {
- ENGINE *e;
- e = ENGINE_by_id(Cryptography_osrandom_engine_id);
- if (e != NULL) {
- ENGINE_free(e);
- return 2;
- } else {
- ERR_clear_error();
- }
-
- e = ENGINE_new();
- if (e == NULL) {
- return 0;
- }
- if(!ENGINE_set_id(e, Cryptography_osrandom_engine_id) ||
- !ENGINE_set_name(e, Cryptography_osrandom_engine_name) ||
- !ENGINE_set_RAND(e, &osrandom_rand) ||
- !ENGINE_set_init_function(e, osrandom_init) ||
- !ENGINE_set_finish_function(e, osrandom_finish)) {
- ENGINE_free(e);
- return 0;
- }
- if (!ENGINE_add(e)) {
- ENGINE_free(e);
- return 0;
- }
- if (!ENGINE_free(e)) {
- return 0;
- }
-
- return 1;
-}
diff --git a/src/_cffi_src/openssl/src/osrandom_engine.h b/src/_cffi_src/openssl/src/osrandom_engine.h
deleted file mode 100644
index 11a3159e..00000000
--- a/src/_cffi_src/openssl/src/osrandom_engine.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifdef _WIN32
-#include <Wincrypt.h>
-#else
-#include <fcntl.h>
-#include <unistd.h>
-#endif
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 51f68899..f05b0515 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -217,7 +217,7 @@ class Backend(object):
self.activate_builtin_random()
# Fetches an engine by id and returns it. This creates a structural
# reference.
- e = self._lib.ENGINE_by_id(self._lib.Cryptography_osrandom_engine_id)
+ e = self._lib.ENGINE_by_id(self._binding._osrandom_engine_id)
assert e != self._ffi.NULL
# Initialize the engine for use. This adds a functional reference.
res = self._lib.ENGINE_init(e)
@@ -872,25 +872,20 @@ class Backend(object):
)
for extension in builder._extensions:
if isinstance(extension.value, x509.BasicConstraints):
- pp, r = _encode_basic_constraints(
- self, extension.value,
- )
+ pp, r = _encode_basic_constraints(self, extension.value)
elif isinstance(extension.value, x509.SubjectAlternativeName):
- pp, r = _encode_subject_alt_name(
- self, extension.value,
- )
+ pp, r = _encode_subject_alt_name(self, extension.value)
else:
raise NotImplementedError('Extension not yet supported.')
obj = _txt2obj(self, extension.oid.dotted_string)
- extension = backend._lib.X509_EXTENSION_create_by_OBJ(
- backend._ffi.NULL,
+ extension = self._lib.X509_EXTENSION_create_by_OBJ(
+ self._ffi.NULL,
obj,
1 if extension.critical else 0,
- _encode_asn1_str(backend, pp[0], r)
+ _encode_asn1_str(self, pp[0], r),
)
- assert extension != backend._ffi.NULL
-
+ assert extension != self._ffi.NULL
res = self._lib.sk_X509_EXTENSION_push(extensions, extension)
assert res == 1
res = self._lib.X509_REQ_add_extensions(x509_req, extensions)
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 804bce66..80e5f2b1 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -86,13 +86,17 @@ def _decode_general_name(backend, gn):
# This is a wildcard name. We need to remove the leading wildcard,
# IDNA decode, then re-add the wildcard. Wildcard characters should
# always be left-most (RFC 2595 section 2.4).
- data = u"*." + idna.decode(data[2:])
+ decoded = u"*." + idna.decode(data[2:])
else:
# Not a wildcard, decode away. If the string has a * in it anywhere
# invalid this will raise an InvalidCodePoint
- data = idna.decode(data)
+ decoded = idna.decode(data)
+ if data.startswith(b"."):
+ # idna strips leading periods. Name constraints can have that
+ # so we need to re-add it. Sigh.
+ decoded = u"." + decoded
- return x509.DNSName(data)
+ return x509.DNSName(decoded)
elif gn.type == backend._lib.GEN_URI:
data = backend._ffi.buffer(
gn.d.uniformResourceIdentifier.data,
@@ -537,6 +541,35 @@ def _decode_issuer_alt_name(backend, ext):
)
+def _decode_name_constraints(backend, ext):
+ nc = backend._ffi.cast(
+ "NAME_CONSTRAINTS *", backend._lib.X509V3_EXT_d2i(ext)
+ )
+ assert nc != backend._ffi.NULL
+ nc = backend._ffi.gc(nc, backend._lib.NAME_CONSTRAINTS_free)
+ permitted = _decode_general_subtrees(backend, nc.permittedSubtrees)
+ excluded = _decode_general_subtrees(backend, nc.excludedSubtrees)
+ return x509.NameConstraints(
+ permitted_subtrees=permitted, excluded_subtrees=excluded
+ )
+
+
+def _decode_general_subtrees(backend, stack_subtrees):
+ if stack_subtrees == backend._ffi.NULL:
+ return None
+
+ num = backend._lib.sk_GENERAL_SUBTREE_num(stack_subtrees)
+ subtrees = []
+
+ for i in range(num):
+ obj = backend._lib.sk_GENERAL_SUBTREE_value(stack_subtrees, i)
+ assert obj != backend._ffi.NULL
+ name = _decode_general_name(backend, obj.base)
+ subtrees.append(name)
+
+ return subtrees
+
+
def _decode_extended_key_usage(backend, ext):
sk = backend._ffi.cast(
"Cryptography_STACK_OF_ASN1_OBJECT *",
@@ -728,6 +761,7 @@ _CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser(
x509.OID_OCSP_NO_CHECK: _decode_ocsp_no_check,
x509.OID_INHIBIT_ANY_POLICY: _decode_inhibit_any_policy,
x509.OID_ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
+ x509.OID_NAME_CONSTRAINTS: _decode_name_constraints,
}
)
diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py
index e0a83972..b7178bb2 100644
--- a/src/cryptography/hazmat/bindings/openssl/binding.py
+++ b/src/cryptography/hazmat/bindings/openssl/binding.py
@@ -4,11 +4,25 @@
from __future__ import absolute_import, division, print_function
+import os
import threading
from cryptography.hazmat.bindings._openssl import ffi, lib
+@ffi.callback("int (*)(unsigned char *, int)", error=-1)
+def _osrandom_rand_bytes(buf, size):
+ signed = ffi.cast("char *", buf)
+ result = os.urandom(size)
+ signed[0:size] = result
+ return 1
+
+
+@ffi.callback("int (*)(void)")
+def _osrandom_rand_status():
+ return 1
+
+
class Binding(object):
"""
OpenSSL API wrapper.
@@ -21,10 +35,42 @@ class Binding(object):
_init_lock = threading.Lock()
_lock_init_lock = threading.Lock()
+ _osrandom_engine_id = ffi.new("const char[]", b"osrandom")
+ _osrandom_engine_name = ffi.new("const char[]", b"osrandom_engine")
+ _osrandom_method = ffi.new(
+ "RAND_METHOD *",
+ dict(bytes=_osrandom_rand_bytes, pseudorand=_osrandom_rand_bytes,
+ status=_osrandom_rand_status)
+ )
+
def __init__(self):
self._ensure_ffi_initialized()
@classmethod
+ def _register_osrandom_engine(cls):
+ assert cls.lib.ERR_peek_error() == 0
+ looked_up_engine = cls.lib.ENGINE_by_id(cls._osrandom_engine_id)
+ if looked_up_engine != ffi.NULL:
+ raise RuntimeError("osrandom engine already registered")
+
+ cls.lib.ERR_clear_error()
+
+ engine = cls.lib.ENGINE_new()
+ assert engine != cls.ffi.NULL
+ try:
+ result = cls.lib.ENGINE_set_id(engine, cls._osrandom_engine_id)
+ assert result == 1
+ result = cls.lib.ENGINE_set_name(engine, cls._osrandom_engine_name)
+ assert result == 1
+ result = cls.lib.ENGINE_set_RAND(engine, cls._osrandom_method)
+ assert result == 1
+ result = cls.lib.ENGINE_add(engine)
+ assert result == 1
+ finally:
+ result = cls.lib.ENGINE_free(engine)
+ assert result == 1
+
+ @classmethod
def _ensure_ffi_initialized(cls):
if cls._lib_loaded:
return
@@ -32,8 +78,7 @@ class Binding(object):
with cls._init_lock:
if not cls._lib_loaded:
cls._lib_loaded = True
- res = cls.lib.Cryptography_add_osrandom_engine()
- assert res != 0
+ cls._register_osrandom_engine()
@classmethod
def init_static_locks(cls):
diff --git a/src/cryptography/hazmat/primitives/ciphers/base.py b/src/cryptography/hazmat/primitives/ciphers/base.py
index 8f3028fc..dae93655 100644
--- a/src/cryptography/hazmat/primitives/ciphers/base.py
+++ b/src/cryptography/hazmat/primitives/ciphers/base.py
@@ -149,6 +149,8 @@ class _CipherContext(object):
class _AEADCipherContext(object):
def __init__(self, ctx):
self._ctx = ctx
+ self._bytes_processed = 0
+ self._aad_bytes_processed = 0
self._tag = None
self._updated = False
@@ -156,6 +158,14 @@ class _AEADCipherContext(object):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
self._updated = True
+ self._bytes_processed += len(data)
+ if self._bytes_processed > self._ctx._mode._MAX_ENCRYPTED_BYTES:
+ raise ValueError(
+ "{0} has a maximum encrypted byte limit of {1}".format(
+ self._ctx._mode.name, self._ctx._mode._MAX_ENCRYPTED_BYTES
+ )
+ )
+
return self._ctx.update(data)
def finalize(self):
@@ -171,6 +181,15 @@ class _AEADCipherContext(object):
raise AlreadyFinalized("Context was already finalized.")
if self._updated:
raise AlreadyUpdated("Update has been called on this context.")
+
+ self._aad_bytes_processed += len(data)
+ if self._aad_bytes_processed > self._ctx._mode._MAX_AAD_BYTES:
+ raise ValueError(
+ "{0} has a maximum AAD byte limit of {0}".format(
+ self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES
+ )
+ )
+
self._ctx.authenticate_additional_data(data)
diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py
index e31c9060..4284042d 100644
--- a/src/cryptography/hazmat/primitives/ciphers/modes.py
+++ b/src/cryptography/hazmat/primitives/ciphers/modes.py
@@ -139,6 +139,8 @@ class CTR(object):
@utils.register_interface(ModeWithAuthenticationTag)
class GCM(object):
name = "GCM"
+ _MAX_ENCRYPTED_BYTES = (2 ** 39 - 256) // 8
+ _MAX_AAD_BYTES = (2 ** 64) // 8
def __init__(self, initialization_vector, tag=None, min_tag_length=16):
# len(initialization_vector) must in [1, 2 ** 64), but it's impossible
diff --git a/tests/conftest.py b/tests/conftest.py
index c4d6b9c1..6599a643 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -18,11 +18,13 @@ def pytest_generate_tests(metafunc):
if "backend" in metafunc.fixturenames:
filtered_backends = []
required = metafunc.function.requires_backend_interface
- required_interfaces = tuple(
+ required_interfaces = [
mark.kwargs["interface"] for mark in required
- )
+ ]
for backend in selected_backends:
- if isinstance(backend, required_interfaces):
+ if all(
+ isinstance(backend, iface) for iface in required_interfaces
+ ):
filtered_backends.append(backend)
# If you pass an empty list to parametrize Bad Things(tm) happen
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index 34fff277..6a2e8a77 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -224,7 +224,7 @@ class TestOpenSSLRandomEngine(object):
backend.activate_osrandom_engine()
current_default = backend._lib.ENGINE_get_default_RAND()
name = backend._lib.ENGINE_get_name(current_default)
- assert name == backend._lib.Cryptography_osrandom_engine_name
+ assert name == backend._binding._osrandom_engine_name
def test_osrandom_engine_is_default(self, tmpdir):
engine_printer = textwrap.dedent(
@@ -258,7 +258,7 @@ class TestOpenSSLRandomEngine(object):
)
osrandom_engine_name = backend._ffi.string(
- backend._lib.Cryptography_osrandom_engine_name
+ backend._binding._osrandom_engine_name
)
assert engine_name.read().encode('ascii') == osrandom_engine_name
@@ -277,7 +277,7 @@ class TestOpenSSLRandomEngine(object):
backend.activate_osrandom_engine()
e = backend._lib.ENGINE_get_default_RAND()
name = backend._lib.ENGINE_get_name(e)
- assert name == backend._lib.Cryptography_osrandom_engine_name
+ assert name == backend._binding._osrandom_engine_name
res = backend._lib.ENGINE_free(e)
assert res == 1
@@ -285,7 +285,7 @@ class TestOpenSSLRandomEngine(object):
e = backend._lib.ENGINE_get_default_RAND()
assert e != backend._ffi.NULL
name = backend._lib.ENGINE_get_name(e)
- assert name == backend._lib.Cryptography_osrandom_engine_name
+ assert name == backend._binding._osrandom_engine_name
res = backend._lib.ENGINE_free(e)
assert res == 1
backend.activate_builtin_random()
@@ -303,13 +303,13 @@ class TestOpenSSLRandomEngine(object):
def test_activate_osrandom_already_default(self):
e = backend._lib.ENGINE_get_default_RAND()
name = backend._lib.ENGINE_get_name(e)
- assert name == backend._lib.Cryptography_osrandom_engine_name
+ assert name == backend._binding._osrandom_engine_name
res = backend._lib.ENGINE_free(e)
assert res == 1
backend.activate_osrandom_engine()
e = backend._lib.ENGINE_get_default_RAND()
name = backend._lib.ENGINE_get_name(e)
- assert name == backend._lib.Cryptography_osrandom_engine_name
+ assert name == backend._binding._osrandom_engine_name
res = backend._lib.ENGINE_free(e)
assert res == 1
diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py
index e6d6fc45..f3f2eaf4 100644
--- a/tests/hazmat/bindings/test_openssl.py
+++ b/tests/hazmat/bindings/test_openssl.py
@@ -4,11 +4,27 @@
from __future__ import absolute_import, division, print_function
+import os
+
import pytest
from cryptography.hazmat.bindings.openssl.binding import Binding
+def skip_if_libre_ssl(openssl_version):
+ if b'LibreSSL' in openssl_version:
+ pytest.skip("LibreSSL hard-codes RAND_bytes to use arc4random.")
+
+
+class TestLibreSkip(object):
+ def test_skip_no(self):
+ assert skip_if_libre_ssl(b"OpenSSL 0.9.8zf 19 Mar 2015") is None
+
+ def test_skip_yes(self):
+ with pytest.raises(pytest.skip.Exception):
+ skip_if_libre_ssl(b"LibreSSL 2.1.6")
+
+
class TestOpenSSL(object):
def test_binding_loads(self):
binding = Binding()
@@ -89,8 +105,22 @@ class TestOpenSSL(object):
def test_add_engine_more_than_once(self):
b = Binding()
- res = b.lib.Cryptography_add_osrandom_engine()
- assert res == 2
+ with pytest.raises(RuntimeError):
+ b._register_osrandom_engine()
+
+ def test_actual_osrandom_bytes(self, monkeypatch):
+ b = Binding()
+ skip_if_libre_ssl(b.ffi.string(b.lib.OPENSSL_VERSION_TEXT))
+ sample_data = (b"\x01\x02\x03\x04" * 4)
+ length = len(sample_data)
+
+ def notrandom(size):
+ assert size == length
+ return sample_data
+ monkeypatch.setattr(os, "urandom", notrandom)
+ buf = b.ffi.new("char[]", length)
+ b.lib.RAND_bytes(buf, length)
+ assert b.ffi.buffer(buf)[0:length] == sample_data
def test_ssl_ctx_options(self):
# Test that we're properly handling 32-bit unsigned on all platforms.
diff --git a/tests/hazmat/primitives/test_aes.py b/tests/hazmat/primitives/test_aes.py
index 4d48e8ad..2c3e5f90 100644
--- a/tests/hazmat/primitives/test_aes.py
+++ b/tests/hazmat/primitives/test_aes.py
@@ -253,3 +253,53 @@ class TestAESModeGCM(object):
computed_ct = encryptor.update(pt) + encryptor.finalize()
assert computed_ct == ct
assert encryptor.tag == tag
+
+ def test_gcm_ciphertext_limit(self, backend):
+ encryptor = base.Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.GCM(b"\x01" * 16),
+ backend=backend
+ ).encryptor()
+ encryptor._bytes_processed = modes.GCM._MAX_ENCRYPTED_BYTES - 16
+ encryptor.update(b"0" * 16)
+ assert (
+ encryptor._bytes_processed == modes.GCM._MAX_ENCRYPTED_BYTES
+ )
+ with pytest.raises(ValueError):
+ encryptor.update(b"0")
+
+ def test_gcm_aad_limit(self, backend):
+ encryptor = base.Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.GCM(b"\x01" * 16),
+ backend=backend
+ ).encryptor()
+ encryptor._aad_bytes_processed = modes.GCM._MAX_AAD_BYTES - 16
+ encryptor.authenticate_additional_data(b"0" * 16)
+ assert encryptor._aad_bytes_processed == modes.GCM._MAX_AAD_BYTES
+ with pytest.raises(ValueError):
+ encryptor.authenticate_additional_data(b"0")
+
+ def test_gcm_ciphertext_increments(self, backend):
+ encryptor = base.Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.GCM(b"\x01" * 16),
+ backend=backend
+ ).encryptor()
+ encryptor.update(b"0" * 8)
+ assert encryptor._bytes_processed == 8
+ encryptor.update(b"0" * 7)
+ assert encryptor._bytes_processed == 15
+ encryptor.update(b"0" * 18)
+ assert encryptor._bytes_processed == 33
+
+ def test_gcm_aad_increments(self, backend):
+ encryptor = base.Cipher(
+ algorithms.AES(b"\x00" * 16),
+ modes.GCM(b"\x01" * 16),
+ backend=backend
+ ).encryptor()
+ encryptor.authenticate_additional_data(b"0" * 8)
+ assert encryptor._aad_bytes_processed == 8
+ encryptor.authenticate_additional_data(b"0" * 18)
+ assert encryptor._aad_bytes_processed == 26
diff --git a/tests/test_interfaces.py b/tests/test_interfaces.py
index 4d571ea6..bdb4a94d 100644
--- a/tests/test_interfaces.py
+++ b/tests/test_interfaces.py
@@ -36,6 +36,8 @@ class TestVerifyInterface(object):
def method(self):
"""Method with no arguments"""
+ # Invoke this to ensure the line is covered
+ NonImplementer().method()
with pytest.raises(InterfaceNotImplemented):
verify_interface(SimpleInterface, NonImplementer)
@@ -51,4 +53,6 @@ class TestVerifyInterface(object):
def property(self):
"""A concrete property"""
+ # Invoke this to ensure the line is covered
+ NonImplementer().property
verify_interface(SimpleInterface, NonImplementer)
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 8601d11d..f71264ea 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -85,7 +85,7 @@ def test_check_backend_support_skip():
supported = pretend.stub(
kwargs={"only_if": lambda backend: False, "skip_message": "Nope"}
)
- item = pretend.stub(keywords={"supported": supported},
+ item = pretend.stub(keywords={"supported": [supported]},
funcargs={"backend": True})
with pytest.raises(pytest.skip.Exception) as exc_info:
check_backend_support(item)
@@ -96,7 +96,7 @@ def test_check_backend_support_no_skip():
supported = pretend.stub(
kwargs={"only_if": lambda backend: True, "skip_message": "Nope"}
)
- item = pretend.stub(keywords={"supported": supported},
+ item = pretend.stub(keywords={"supported": [supported]},
funcargs={"backend": True})
assert check_backend_support(item) is None
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 6d91ba41..0ef84e79 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -2033,6 +2033,88 @@ class TestNameConstraints(object):
assert nc != object()
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestNameConstraintsExtension(object):
+ def test_permitted_excluded(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "nc_permitted_excluded_2.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ nc = cert.extensions.get_extension_for_oid(
+ x509.OID_NAME_CONSTRAINTS
+ ).value
+ assert nc == x509.NameConstraints(
+ permitted_subtrees=[
+ x509.DNSName(u"zombo.local"),
+ ],
+ excluded_subtrees=[
+ x509.DirectoryName(x509.Name([
+ x509.NameAttribute(x509.OID_COMMON_NAME, u"zombo")
+ ]))
+ ]
+ )
+
+ def test_permitted(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "nc_permitted_2.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ nc = cert.extensions.get_extension_for_oid(
+ x509.OID_NAME_CONSTRAINTS
+ ).value
+ assert nc == x509.NameConstraints(
+ permitted_subtrees=[
+ x509.DNSName(u"zombo.local"),
+ ],
+ excluded_subtrees=None
+ )
+
+ def test_permitted_with_leading_period(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "nc_permitted.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ nc = cert.extensions.get_extension_for_oid(
+ x509.OID_NAME_CONSTRAINTS
+ ).value
+ assert nc == x509.NameConstraints(
+ permitted_subtrees=[
+ x509.DNSName(u".cryptography.io"),
+ x509.UniformResourceIdentifier(u"ftp://cryptography.test")
+ ],
+ excluded_subtrees=None
+ )
+
+ def test_excluded_with_leading_period(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "nc_excluded.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ nc = cert.extensions.get_extension_for_oid(
+ x509.OID_NAME_CONSTRAINTS
+ ).value
+ assert nc == x509.NameConstraints(
+ permitted_subtrees=None,
+ excluded_subtrees=[
+ x509.DNSName(u".cryptography.io"),
+ x509.UniformResourceIdentifier(u"gopher://cryptography.test")
+ ]
+ )
+
+
class TestDistributionPoint(object):
def test_distribution_point_full_name_not_general_names(self):
with pytest.raises(TypeError):
diff --git a/tests/utils.py b/tests/utils.py
index 8be5c1fa..5083d48c 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -53,10 +53,11 @@ def skip_if_empty(backend_list, required_interfaces):
def check_backend_support(item):
supported = item.keywords.get("supported")
if supported and "backend" in item.funcargs:
- if not supported.kwargs["only_if"](item.funcargs["backend"]):
- pytest.skip("{0} ({1})".format(
- supported.kwargs["skip_message"], item.funcargs["backend"]
- ))
+ for mark in supported:
+ if not mark.kwargs["only_if"](item.funcargs["backend"]):
+ pytest.skip("{0} ({1})".format(
+ mark.kwargs["skip_message"], item.funcargs["backend"]
+ ))
elif supported:
raise ValueError("This mark is only available on methods that take a "
"backend")
diff --git a/vectors/cryptography_vectors/x509/custom/nc_permitted_2.pem b/vectors/cryptography_vectors/x509/custom/nc_permitted_2.pem
new file mode 100644
index 00000000..05a904f5
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/nc_permitted_2.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAcSgAwIBAgITBm/6kAXHpw8f3guDBcRXNLfl5DANBgkqhkiG9w0BAQsF
+ADAXMRUwEwYDVQQDDAxjcnlwdG9ncmFwaHkwHhcNMTUwNjI5MDkzMjQ5WhcNMTYw
+NjI4MDkzMjQ5WjAXMRUwEwYDVQQDDAxjcnlwdG9ncmFwaHkwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCylTa0WkLvIXB4sWoPdv5iL3idlVHKR+ncODKL
+nwQ2Jtd990MfakOFRLrJFF1tfPL4qyRbbyMyrgCOoKBCAuIdBZfBDH3JWFjxGy8J
+Yls8yVeAVKreV18HmLvAsBL3bnr7Gk3vpznrfoG5rn5T/fL0cqqTXFV8zQhjHiEo
+zftSaoq0LOxsSgFdxXS8e8K6RMvLCZPcMpI4fo1Kq2QBT2J1x1/Hq/VnK132cs0g
+TOyiTyyJfvRmlqdXowh7Jf8LQB4mM6gc023fEdQ+HH6JYX1vDQVxaiTM6KMYJNv/
+l4gchP3jknOfZffwGGdXQrtUMhQmltnSqV5nY/G2OGm/Z0pdAgMBAAGjITAfMB0G
+A1UdHgEB/wQTMBGgDzANggt6b21iby5sb2NhbDANBgkqhkiG9w0BAQsFAAOCAQEA
+WcDkqLUsvIBfTzKncOSiy08lcwu+kq/8rybh4HoHEimcy47M+XLPnXqwA7jedz8M
+Znog64O9wZ1olWs+GGrGcJAth2BqdNtRvb6/o2Hq29vNbCQeYRlOTdNzGnb5v6fB
+HEPvuB7UNKyaJ2tF50oxqhg8ojgauX3fuanCtR9Obx/2U8e8zfBIauX13XfpoCyt
+efeL97kYz+XIQwG8TvXpNdHO0QjmA/ToR7E5BbSo2e4cicKEomtLhKI7EXa+Ofwg
+HoyVC8wl97nm7mwI7iFYK5f8YoqwILxKEP6O9+pZEOveqdKfx4+WAgeGyDvBwAjf
+Ej8vkawtdgV/96ajsIqzDQ==
+-----END CERTIFICATE-----
diff --git a/vectors/cryptography_vectors/x509/custom/nc_permitted_excluded_2.pem b/vectors/cryptography_vectors/x509/custom/nc_permitted_excluded_2.pem
new file mode 100644
index 00000000..2cdf1d68
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/nc_permitted_excluded_2.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC9DCCAdygAwIBAgITBm/3q66sET2C+Ko/TLr1EHnvdTANBgkqhkiG9w0BAQsF
+ADAXMRUwEwYDVQQDDAxjcnlwdG9ncmFwaHkwHhcNMTUwNjI5MDY0ODQ4WhcNMTYw
+NjI4MDY0ODQ4WjAXMRUwEwYDVQQDDAxjcnlwdG9ncmFwaHkwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCylTa0WkLvIXB4sWoPdv5iL3idlVHKR+ncODKL
+nwQ2Jtd990MfakOFRLrJFF1tfPL4qyRbbyMyrgCOoKBCAuIdBZfBDH3JWFjxGy8J
+Yls8yVeAVKreV18HmLvAsBL3bnr7Gk3vpznrfoG5rn5T/fL0cqqTXFV8zQhjHiEo
+zftSaoq0LOxsSgFdxXS8e8K6RMvLCZPcMpI4fo1Kq2QBT2J1x1/Hq/VnK132cs0g
+TOyiTyyJfvRmlqdXowh7Jf8LQB4mM6gc023fEdQ+HH6JYX1vDQVxaiTM6KMYJNv/
+l4gchP3jknOfZffwGGdXQrtUMhQmltnSqV5nY/G2OGm/Z0pdAgMBAAGjOTA3MDUG
+A1UdHgEB/wQrMCmgDzANggt6b21iby5sb2NhbKEWMBSkEjAQMQ4wDAYDVQQDDAV6
+b21ibzANBgkqhkiG9w0BAQsFAAOCAQEAaSyuJlNVZkkwHn4V9EglOTm6DC/lzrLm
+1y/qcXsY2NXgCRfpZal0lx25M7Dl2G1IOBG+Ub1/ua0NASlpd6BeZ4prmcD4OBib
+oAhMJxt8QNNwkcMG5PnI6reQz5MiRwGOCEAZeX1opIijn/tO49RliEnEQCKbsvdr
+d+0ieNhLdoxazW/k3UCu+Vdd1b3TOLERrhm/xGj2W9AhWAv7GIovhBGGfuD6BFmC
+uHjxoG0So//NiHTfZ9eukgW3rNSbjQjtnC8BsRzUdhX/YBvw+SKkeVL2oz7+lRgD
+fhba3FtwUfCIX3y/UAc0E0+x9bLFDyQXYNHAXq+q72sOkLXgAH8bfQ==
+-----END CERTIFICATE-----
diff --git a/vectors/cryptography_vectors/x509/custom/nc_single_ip_netmask.pem b/vectors/cryptography_vectors/x509/custom/nc_single_ip_netmask.pem
new file mode 100644
index 00000000..2931b6b9
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/nc_single_ip_netmask.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/TCCAeWgAwIBAgITBnA4pkis5m3OGusBaihd9qH0hzANBgkqhkiG9w0BAQsF
+ADAXMRUwEwYDVQQDDAxjcnlwdG9ncmFwaHkwHhcNMTUwNzAxMjAxNDAwWhcNMTYw
+NjMwMjAxNDAwWjAXMRUwEwYDVQQDDAxjcnlwdG9ncmFwaHkwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCYyaGtu90vcm+jN+SoQHXxWMQyplY1neL9KjfE
++TsKKcy8TKJEqlT8qZr6bIL3KVbTIiYO8bCW9fHSMgHWrmtr37LlFoQ3emcLfDbM
+kybmOolAxA78im0L2BIW1wT2iSHh1p/ZO5QLdt+e8zP5AkZAnXCZk912RcJYyGUW
+7JQzzRfEANSLE9Gmh78NsxWNI1Ipc3dhyuk3+YHwePGCzLCeXCiF4FHGNMg8Drtr
+rENNHZjHJCbMLfK9irHV5Xh1FHTK8xlqEq+YecpqboUyqgWVOOvpxUxiKagfp//Z
++iFDC1+GgpuupzFUiHPSVCZGMnE3bHvIBOkoHkNu7kNK7VX3AgMBAAGjQjBAMD4G
+A1UdHgEB/wQ0MDKgMDAihyAA/wAAAAAAAAAAAAAAAAAA////////////////////
+/zAKhwjAqAAB/////zANBgkqhkiG9w0BAQsFAAOCAQEAXSDmonnBpivsW/NKE85c
+1ho449K98+1cFUD51VeK42oPUd0GRQCU3ETYJ5YyK7OMoQqe4LTtNDx6ZCF+6z/r
+tZctfdpwRmqh2ebGn3qDs1FAckkwwSCRtkJTdgznmtO680Ls5GveNFrgYJkYfFjj
+OWpzCypse/3j3uVgSakmjBRS4BOsyX4o7trN+k1MmQOrMpWEtLlmrZJpM66sgP0j
+WpI95l4paIMpkFarwCCQfJCNBpl7Uol+BD4vJvf/J7f7ZwxQMEWCBPnYk3EKMnKa
+aCsmqRMV1W7SzxL07dHMzWnsC/I5oJNj4HdthGcIJf1Jut9A9KFVodOJAxKOziz2
+aQ==
+-----END CERTIFICATE-----