aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cryptography/hazmat/backends/openssl/backend.py204
-rw-r--r--cryptography/hazmat/backends/openssl/ec.py175
-rw-r--r--cryptography/hazmat/bindings/openssl/err.py19
-rw-r--r--cryptography/hazmat/primitives/asymmetric/dsa.py71
-rw-r--r--docs/development/c-bindings.rst192
-rw-r--r--docs/development/index.rst1
-rw-r--r--docs/development/submitting-patches.rst66
-rw-r--r--docs/hazmat/primitives/asymmetric/dsa.rst67
-rw-r--r--docs/hazmat/primitives/interfaces.rst89
-rw-r--r--docs/spelling_wordlist.txt4
-rw-r--r--tests/hazmat/backends/test_openssl.py5
-rw-r--r--tests/hazmat/primitives/test_dsa.py60
-rw-r--r--tests/hazmat/primitives/test_ec.py29
13 files changed, 683 insertions, 299 deletions
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 53d92be3..5a1a2118 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -30,6 +30,10 @@ from cryptography.hazmat.backends.interfaces import (
HashBackend, PBKDF2HMACBackend, PKCS8SerializationBackend, RSABackend,
TraditionalOpenSSLSerializationBackend
)
+from cryptography.hazmat.backends.openssl.ec import (
+ _ECDSASignatureContext, _ECDSAVerificationContext,
+ _EllipticCurvePrivateKey, _EllipticCurvePublicKey
+)
from cryptography.hazmat.bindings.openssl.binding import Binding
from cryptography.hazmat.primitives import hashes, interfaces
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
@@ -930,8 +934,28 @@ class Backend(object):
if self._lib.Cryptography_HAS_EC != 1:
return False
- curves = self._supported_curves()
- return curve.name.encode("ascii") in curves
+ try:
+ curve_nid = self._elliptic_curve_to_nid(curve)
+ except UnsupportedAlgorithm:
+ curve_nid = self._lib.NID_undef
+
+ ctx = self._lib.EC_GROUP_new_by_curve_name(curve_nid)
+
+ if ctx == self._ffi.NULL:
+ errors = self._consume_errors()
+ assert (
+ curve_nid == self._lib.NID_undef or
+ errors[0][1:] == (
+ self._lib.ERR_LIB_EC,
+ self._lib.EC_F_EC_GROUP_NEW_BY_CURVE_NAME,
+ self._lib.EC_R_UNKNOWN_GROUP
+ )
+ )
+ return False
+ else:
+ assert curve_nid != self._lib.NID_undef
+ self._lib.EC_GROUP_free(ctx)
+ return True
def elliptic_curve_signature_algorithm_supported(
self, signature_algorithm, curve
@@ -952,30 +976,6 @@ class Backend(object):
return self.elliptic_curve_supported(curve)
- def _supported_curves(self):
- if self._lib.Cryptography_HAS_EC != 1:
- return []
-
- num_curves = self._lib.EC_get_builtin_curves(self._ffi.NULL, 0)
- curve_array = self._ffi.new("EC_builtin_curve[]", num_curves)
- num_curves_assigned = self._lib.EC_get_builtin_curves(
- curve_array, num_curves)
- assert num_curves == num_curves_assigned
-
- curves = [
- self._ffi.string(self._lib.OBJ_nid2sn(curve.nid)).decode()
- for curve in curve_array
- ]
-
- curve_aliases = {
- "prime192v1": "secp192r1",
- "prime256v1": "secp256r1"
- }
- return [
- curve_aliases.get(curve, curve)
- for curve in curves
- ]
-
def _create_ecdsa_signature_ctx(self, private_key, ecdsa):
return _ECDSASignatureContext(self, private_key, ecdsa.algorithm)
@@ -1983,158 +1983,6 @@ class _CMACContext(object):
)
-def _truncate_digest_for_ecdsa(ec_key_cdata, digest, backend):
- _lib = backend._lib
- _ffi = backend._ffi
-
- digest_len = len(digest)
-
- group = _lib.EC_KEY_get0_group(ec_key_cdata)
-
- bn_ctx = _lib.BN_CTX_new()
- assert bn_ctx != _ffi.NULL
- bn_ctx = _ffi.gc(bn_ctx, _lib.BN_CTX_free)
-
- order = _lib.BN_CTX_get(bn_ctx)
- assert order != _ffi.NULL
-
- res = _lib.EC_GROUP_get_order(group, order, bn_ctx)
- assert res == 1
-
- order_bits = _lib.BN_num_bits(order)
-
- if 8 * digest_len > order_bits:
- digest_len = (order_bits + 7) // 8
- digest = digest[:digest_len]
-
- if 8 * digest_len > order_bits:
- rshift = 8 - (order_bits & 0x7)
- assert rshift > 0 and rshift < 8
-
- mask = 0xFF >> rshift << rshift
-
- # Set the bottom rshift bits to 0
- digest = digest[:-1] + six.int2byte(six.byte2int(digest[-1]) & mask)
-
- return digest
-
-
-@utils.register_interface(interfaces.AsymmetricSignatureContext)
-class _ECDSASignatureContext(object):
- def __init__(self, backend, private_key, algorithm):
- self._backend = backend
- self._private_key = private_key
- self._digest = hashes.Hash(algorithm, backend)
-
- def update(self, data):
- self._digest.update(data)
-
- def finalize(self):
- ec_key = self._private_key._ec_key
-
- digest = self._digest.finalize()
-
- digest = _truncate_digest_for_ecdsa(ec_key, digest, self._backend)
-
- max_size = self._backend._lib.ECDSA_size(ec_key)
- assert max_size > 0
-
- sigbuf = self._backend._ffi.new("char[]", max_size)
- siglen_ptr = self._backend._ffi.new("unsigned int[]", 1)
- res = self._backend._lib.ECDSA_sign(
- 0,
- digest,
- len(digest),
- sigbuf,
- siglen_ptr,
- ec_key
- )
- assert res == 1
- return self._backend._ffi.buffer(sigbuf)[:siglen_ptr[0]]
-
-
-@utils.register_interface(interfaces.AsymmetricVerificationContext)
-class _ECDSAVerificationContext(object):
- def __init__(self, backend, public_key, signature, algorithm):
- self._backend = backend
- self._public_key = public_key
- self._signature = signature
- self._digest = hashes.Hash(algorithm, backend)
-
- def update(self, data):
- self._digest.update(data)
-
- def verify(self):
- ec_key = self._public_key._ec_key
-
- digest = self._digest.finalize()
-
- digest = _truncate_digest_for_ecdsa(ec_key, digest, self._backend)
-
- res = self._backend._lib.ECDSA_verify(
- 0,
- digest,
- len(digest),
- self._signature,
- len(self._signature),
- ec_key
- )
- if res != 1:
- self._backend._consume_errors()
- raise InvalidSignature
- return True
-
-
-@utils.register_interface(interfaces.EllipticCurvePrivateKey)
-class _EllipticCurvePrivateKey(object):
- def __init__(self, backend, ec_key_cdata, curve):
- self._backend = backend
- self._ec_key = ec_key_cdata
- self._curve = curve
-
- @property
- def curve(self):
- return self._curve
-
- def signer(self, signature_algorithm):
- if isinstance(signature_algorithm, ec.ECDSA):
- return self._backend._create_ecdsa_signature_ctx(
- self, signature_algorithm)
- else:
- raise UnsupportedAlgorithm(
- "Unsupported elliptic curve signature algorithm.",
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
-
- def public_key(self):
- public_ec_key = self._backend._public_ec_key_from_private_ec_key(
- self._ec_key
- )
-
- return _EllipticCurvePublicKey(
- self._backend, public_ec_key, self._curve)
-
-
-@utils.register_interface(interfaces.EllipticCurvePublicKey)
-class _EllipticCurvePublicKey(object):
- def __init__(self, backend, ec_key_cdata, curve):
- self._backend = backend
- self._ec_key = ec_key_cdata
- self._curve = curve
-
- @property
- def curve(self):
- return self._curve
-
- def verifier(self, signature, signature_algorithm):
- if isinstance(signature_algorithm, ec.ECDSA):
- return self._backend._create_ecdsa_verification_ctx(
- self, signature, signature_algorithm)
- else:
- raise UnsupportedAlgorithm(
- "Unsupported elliptic curve signature algorithm.",
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
-
-
@utils.register_interface(RSAPrivateKeyWithNumbers)
class _RSAPrivateKey(object):
def __init__(self, backend, rsa_cdata):
diff --git a/cryptography/hazmat/backends/openssl/ec.py b/cryptography/hazmat/backends/openssl/ec.py
new file mode 100644
index 00000000..892d20ea
--- /dev/null
+++ b/cryptography/hazmat/backends/openssl/ec.py
@@ -0,0 +1,175 @@
+# 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 (
+ InvalidSignature, UnsupportedAlgorithm, _Reasons
+)
+from cryptography.hazmat.primitives import hashes, interfaces
+from cryptography.hazmat.primitives.asymmetric import ec
+
+
+def _truncate_digest_for_ecdsa(ec_key_cdata, digest, backend):
+ _lib = backend._lib
+ _ffi = backend._ffi
+
+ digest_len = len(digest)
+
+ group = _lib.EC_KEY_get0_group(ec_key_cdata)
+
+ bn_ctx = _lib.BN_CTX_new()
+ assert bn_ctx != _ffi.NULL
+ bn_ctx = _ffi.gc(bn_ctx, _lib.BN_CTX_free)
+
+ order = _lib.BN_CTX_get(bn_ctx)
+ assert order != _ffi.NULL
+
+ res = _lib.EC_GROUP_get_order(group, order, bn_ctx)
+ assert res == 1
+
+ order_bits = _lib.BN_num_bits(order)
+
+ if 8 * digest_len > order_bits:
+ digest_len = (order_bits + 7) // 8
+ digest = digest[:digest_len]
+
+ if 8 * digest_len > order_bits:
+ rshift = 8 - (order_bits & 0x7)
+ assert rshift > 0 and rshift < 8
+
+ mask = 0xFF >> rshift << rshift
+
+ # Set the bottom rshift bits to 0
+ digest = digest[:-1] + six.int2byte(six.indexbytes(digest, -1) & mask)
+
+ return digest
+
+
+@utils.register_interface(interfaces.AsymmetricSignatureContext)
+class _ECDSASignatureContext(object):
+ def __init__(self, backend, private_key, algorithm):
+ self._backend = backend
+ self._private_key = private_key
+ self._digest = hashes.Hash(algorithm, backend)
+
+ def update(self, data):
+ self._digest.update(data)
+
+ def finalize(self):
+ ec_key = self._private_key._ec_key
+
+ digest = self._digest.finalize()
+
+ digest = _truncate_digest_for_ecdsa(ec_key, digest, self._backend)
+
+ max_size = self._backend._lib.ECDSA_size(ec_key)
+ assert max_size > 0
+
+ sigbuf = self._backend._ffi.new("char[]", max_size)
+ siglen_ptr = self._backend._ffi.new("unsigned int[]", 1)
+ res = self._backend._lib.ECDSA_sign(
+ 0,
+ digest,
+ len(digest),
+ sigbuf,
+ siglen_ptr,
+ ec_key
+ )
+ assert res == 1
+ return self._backend._ffi.buffer(sigbuf)[:siglen_ptr[0]]
+
+
+@utils.register_interface(interfaces.AsymmetricVerificationContext)
+class _ECDSAVerificationContext(object):
+ def __init__(self, backend, public_key, signature, algorithm):
+ self._backend = backend
+ self._public_key = public_key
+ self._signature = signature
+ self._digest = hashes.Hash(algorithm, backend)
+
+ def update(self, data):
+ self._digest.update(data)
+
+ def verify(self):
+ ec_key = self._public_key._ec_key
+
+ digest = self._digest.finalize()
+
+ digest = _truncate_digest_for_ecdsa(ec_key, digest, self._backend)
+
+ res = self._backend._lib.ECDSA_verify(
+ 0,
+ digest,
+ len(digest),
+ self._signature,
+ len(self._signature),
+ ec_key
+ )
+ if res != 1:
+ self._backend._consume_errors()
+ raise InvalidSignature
+ return True
+
+
+@utils.register_interface(interfaces.EllipticCurvePrivateKey)
+class _EllipticCurvePrivateKey(object):
+ def __init__(self, backend, ec_key_cdata, curve):
+ self._backend = backend
+ self._ec_key = ec_key_cdata
+ self._curve = curve
+
+ @property
+ def curve(self):
+ return self._curve
+
+ def signer(self, signature_algorithm):
+ if isinstance(signature_algorithm, ec.ECDSA):
+ return self._backend._create_ecdsa_signature_ctx(
+ self, signature_algorithm)
+ else:
+ raise UnsupportedAlgorithm(
+ "Unsupported elliptic curve signature algorithm.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+
+ def public_key(self):
+ public_ec_key = self._backend._public_ec_key_from_private_ec_key(
+ self._ec_key
+ )
+
+ return _EllipticCurvePublicKey(
+ self._backend, public_ec_key, self._curve)
+
+
+@utils.register_interface(interfaces.EllipticCurvePublicKey)
+class _EllipticCurvePublicKey(object):
+ def __init__(self, backend, ec_key_cdata, curve):
+ self._backend = backend
+ self._ec_key = ec_key_cdata
+ self._curve = curve
+
+ @property
+ def curve(self):
+ return self._curve
+
+ def verifier(self, signature, signature_algorithm):
+ if isinstance(signature_algorithm, ec.ECDSA):
+ return self._backend._create_ecdsa_verification_ctx(
+ self, signature, signature_algorithm)
+ else:
+ raise UnsupportedAlgorithm(
+ "Unsupported elliptic curve signature algorithm.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
diff --git a/cryptography/hazmat/bindings/openssl/err.py b/cryptography/hazmat/bindings/openssl/err.py
index 9c9b45c2..232060a2 100644
--- a/cryptography/hazmat/bindings/openssl/err.py
+++ b/cryptography/hazmat/bindings/openssl/err.py
@@ -21,6 +21,7 @@ TYPES = """
static const int Cryptography_HAS_REMOVE_THREAD_STATE;
static const int Cryptography_HAS_098H_ERROR_CODES;
static const int Cryptography_HAS_098C_CAMELLIA_CODES;
+static const int Cryptography_HAS_EC_CODES;
struct ERR_string_data_st {
unsigned long error;
@@ -29,6 +30,7 @@ struct ERR_string_data_st {
typedef struct ERR_string_data_st ERR_STRING_DATA;
static const int ERR_LIB_EVP;
+static const int ERR_LIB_EC;
static const int ERR_LIB_PEM;
static const int ERR_LIB_ASN1;
static const int ERR_LIB_RSA;
@@ -172,6 +174,10 @@ static const int EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM;
static const int EVP_R_WRONG_FINAL_BLOCK_LENGTH;
static const int EVP_R_WRONG_PUBLIC_KEY_TYPE;
+static const int EC_F_EC_GROUP_NEW_BY_CURVE_NAME;
+
+static const int EC_R_UNKNOWN_GROUP;
+
static const int PEM_F_D2I_PKCS8PRIVATEKEY_BIO;
static const int PEM_F_D2I_PKCS8PRIVATEKEY_FP;
static const int PEM_F_DO_PK8PKEY;
@@ -306,6 +312,15 @@ static const long Cryptography_HAS_098C_CAMELLIA_CODES = 0;
static const int EVP_F_CAMELLIA_INIT_KEY = 0;
static const int EVP_R_CAMELLIA_KEY_SETUP_FAILED = 0;
#endif
+
+// OpenSSL without EC. e.g. RHEL
+#ifndef OPENSSL_NO_EC
+static const long Cryptography_HAS_EC_CODES = 1;
+#else
+static const long Cryptography_HAS_EC_CODES = 0;
+static const int EC_R_UNKNOWN_GROUP = 0;
+static const int EC_F_EC_GROUP_NEW_BY_CURVE_NAME = 0;
+#endif
"""
CONDITIONAL_NAMES = {
@@ -324,5 +339,9 @@ CONDITIONAL_NAMES = {
"Cryptography_HAS_098C_CAMELLIA_CODES": [
"EVP_F_CAMELLIA_INIT_KEY",
"EVP_R_CAMELLIA_KEY_SETUP_FAILED"
+ ],
+ "Cryptography_HAS_EC_CODES": [
+ "EC_R_UNKNOWN_GROUP",
+ "EC_F_EC_GROUP_NEW_BY_CURVE_NAME"
]
}
diff --git a/cryptography/hazmat/primitives/asymmetric/dsa.py b/cryptography/hazmat/primitives/asymmetric/dsa.py
index a9ae9ecb..4d78679e 100644
--- a/cryptography/hazmat/primitives/asymmetric/dsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/dsa.py
@@ -181,3 +181,74 @@ class DSAPublicKey(object):
def parameters(self):
return DSAParameters(self._modulus, self._subgroup_order,
self._generator)
+
+
+class DSAParameterNumbers(object):
+ def __init__(self, p, q, g):
+ if (
+ not isinstance(p, six.integer_types) or
+ not isinstance(q, six.integer_types) or
+ not isinstance(g, six.integer_types)
+ ):
+ raise TypeError(
+ "DSAParameterNumbers p, q, and g arguments must be integers."
+ )
+
+ self._p = p
+ self._q = q
+ self._g = g
+
+ @property
+ def p(self):
+ return self._p
+
+ @property
+ def q(self):
+ return self._q
+
+ @property
+ def g(self):
+ return self._g
+
+
+class DSAPublicNumbers(object):
+ def __init__(self, y, parameter_numbers):
+ if not isinstance(y, six.integer_types):
+ raise TypeError("DSAPublicNumbers y argument must be an integer.")
+
+ if not isinstance(parameter_numbers, DSAParameterNumbers):
+ raise TypeError(
+ "parameter_numbers must be a DSAParameterNumbers instance."
+ )
+
+ self._y = y
+ self._parameter_numbers = parameter_numbers
+
+ @property
+ def y(self):
+ return self._y
+
+ @property
+ def parameter_numbers(self):
+ return self._parameter_numbers
+
+
+class DSAPrivateNumbers(object):
+ def __init__(self, x, public_numbers):
+ if not isinstance(x, six.integer_types):
+ raise TypeError("DSAPrivateNumbers x argument must be an integer.")
+
+ if not isinstance(public_numbers, DSAPublicNumbers):
+ raise TypeError(
+ "public_numbers must be a DSAPublicNumbers instance."
+ )
+ self._public_numbers = public_numbers
+ self._x = x
+
+ @property
+ def x(self):
+ return self._x
+
+ @property
+ def public_numbers(self):
+ return self._public_numbers
diff --git a/docs/development/c-bindings.rst b/docs/development/c-bindings.rst
new file mode 100644
index 00000000..56963379
--- /dev/null
+++ b/docs/development/c-bindings.rst
@@ -0,0 +1,192 @@
+C bindings
+==========
+
+C bindings are bindings to C libraries, using cffi_ whenever possible.
+
+.. _cffi: http://cffi.readthedocs.org
+
+Bindings live in :py:mod:`cryptography.hazmat.bindings`.
+
+Style guide
+-----------
+
+Don't name parameters:
+
+.. code-block:: c
+
+ /* Good */
+ long f(long);
+ /* Bad */
+ long f(long x);
+
+...unless they're inside a struct:
+
+.. code-block:: c
+
+ struct my_struct {
+ char *name;
+ int number;
+ ...;
+ };
+
+Include ``void`` if the function takes no arguments:
+
+.. code-block:: c
+
+ /* Good */
+ long f(void);
+ /* Bad */
+ long f();
+
+Wrap lines at 80 characters like so:
+
+.. code-block:: c
+
+ /* Pretend this went to 80 characters */
+ long f(long, long,
+ int *)
+
+Include a space after commas between parameters:
+
+.. code-block:: c
+
+ /* Good */
+ long f(int, char *)
+ /* Bad */
+ long f(int,char *)
+
+Use C-style ``/* */`` comments instead of C++-style ``//``:
+
+.. code-block:: c
+
+ // Bad
+ /* Good */
+
+Values set by ``#define`` should be assigned the appropriate type. If you see
+this:
+
+.. code-block:: c
+
+ #define SOME_INTEGER_LITERAL 0x0;
+ #define SOME_UNSIGNED_INTEGER_LITERAL 0x0001U;
+ #define SOME_STRING_LITERAL "hello";
+
+...it should be added to the bindings like so:
+
+.. code-block:: c
+
+ static const int SOME_INTEGER_LITERAL;
+ static const unsigned int SOME_UNSIGNED_INTEGER_LITERAL;
+ static const char *const SOME_STRING_LITERAL;
+
+Adding constant, types, functions...
+------------------------------------
+
+You can create bindings for any name that exists in some version of
+the library you're binding against. However, the project also has to
+keep supporting older versions of the library. In order to achieve
+this, binding modules have ``CUSTOMIZATIONS`` and
+``CONDITIONAL_NAMES`` constants.
+
+Let's say you want to enable quantum transmogrification. The upstream
+library implements this as the following API::
+
+ static const int QM_TRANSMOGRIFICATION_ALIGNMENT_LEFT;
+ static const int QM_TRANSMOGRIFICATION_ALIGNMENT_RIGHT;
+ typedef ... QM_TRANSMOGRIFICATION_CTX;
+ int QM_transmogrify(QM_TRANSMOGRIFICATION_CTX *, int);
+
+To start, create a new constant that defines if the *actual* library
+has the feature you want, and add it to ``TYPES``::
+
+ static const long Cryptography_HAS_QUANTUM_TRANSMOGRIFICATION;
+
+This should start with ``Cryptography_``, since we're adding it in
+this library. This prevents namespace collisions.
+
+Then, define the actual features (constants, types, functions...) you
+want to expose. If it's a constant, just add it to ``TYPES``::
+
+ static const int QM_TRANSMOGRIFICATION_ALIGNMENT_LEFT;
+ static const int QM_TRANSMOGRIFICATION_ALIGNMENT_RIGHT;
+
+If it's a struct, add it to ``TYPES`` as well. The following is an
+opaque struct::
+
+ typedef ... QM_TRANSMOGRIFICATION_CTX;
+
+... but you can also make some or all items in the struct accessible::
+
+ typedef struct {
+ /* Fundamental constant k for your particular universe */
+ BIGNUM *k;
+ ...;
+ } QM_TRANSMOGRIFICATION_CTX;
+
+Confusingly, functions that aren't always available on all supported
+versions of the library, should be defined in ``MACROS`` and *not* in
+``FUNCTIONS``. Fortunately, you just have to copy the signature::
+
+ int QM_transmogrify(QM_TRANSMOGRIFICATION_CTX *, int);
+
+Then, we define the ``CUSTOMIZATIONS`` entry. To do that, we have to
+come up with a C preprocessor expression that decides whether or not a
+feature exists in the library. For example::
+
+ #ifdef QM_transmogrify
+
+Then, we set the flag that signifies the feature exists::
+
+ static const long Cryptography_HAS_QUANTUM_TRANSMOGRIFICATION = 1;
+
+Otherwise, we set that flag to 0::
+
+ #else
+ static const long Cryptography_HAS_QUANTUM_TRANSMOGRIFICATION = 0;
+
+Then, in that ``#else`` block, we define the names that aren't
+available as dummy values. For an integer constant, use 0::
+
+ static const int QM_TRANSMOGRIFICATION_ALIGNMENT_LEFT = 0;
+ static const int QM_TRANSMOGRIFICATION_ALIGNMENT_RIGHT = 0;
+
+For a function, it's a bit trickier. You have to define a function
+pointer of the appropriate type to be NULL::
+
+ int (*QM_transmogrify)(QM_TRANSMOGRIFICATION_CTX *, int) = NULL;
+
+(To do that, copy the signature, put a ``*`` in front of the function
+name and wrap it in parentheses, and then put ``= NULL`` at the end).
+
+Note how types don't need to be conditionally defined, as long as all
+the necessarily type definitions are in place.
+
+Finally, add an entry to ``CONDITIONAL_NAMES`` with all of the things
+you want to conditionally export::
+
+ CONDITIONAL_NAMES = {
+ ...
+ "Cryptography_HAS_QUANTUM_TRANSMOGRIFICATION": [
+ "QM_TRANSMOGRIFICATION_ALIGNMENT_LEFT",
+ "QM_TRANSMOGRIFICATION_ALIGNMENT_RIGHT",
+ "QM_transmogrify"
+ ]
+ }
+
+Caveats
+~~~~~~~
+
+Sometimes, a set of loosely related features are added in the same
+version, and it's impractical to create ``#ifdef`` statements for each
+one. In that case, it may make sense to either check for a particular
+version. For example, to check for OpenSSL 1.0.0 or newer::
+
+ #if OPENSSL_VERSION_NUMBER >= 0x10000000L
+
+Sometimes, the version of a library on a particular platform will have
+features that you thought it wouldn't, based on its version.
+Occasionally, packagers appear to ship arbitrary VCS checkouts. As a
+result, sometimes you may have to add separate ``#ifdef`` statements
+for particular features. This kind of issue is typically only caught
+by running the tests on a wide variety of systems, which is the job of
+our continuous integration infrastructure.
diff --git a/docs/development/index.rst b/docs/development/index.rst
index 50b60900..f9bc9eea 100644
--- a/docs/development/index.rst
+++ b/docs/development/index.rst
@@ -14,6 +14,7 @@ bug check out `what to put in your bug report`_.
submitting-patches
reviewing-patches
test-vectors
+ c-bindings
.. _`GitHub`: https://github.com/pyca/cryptography
.. _`what to put in your bug report`: http://www.contribution-guide.org/#what-to-put-in-your-bug-report
diff --git a/docs/development/submitting-patches.rst b/docs/development/submitting-patches.rst
index b7f43283..fe2df431 100644
--- a/docs/development/submitting-patches.rst
+++ b/docs/development/submitting-patches.rst
@@ -75,70 +75,8 @@ specifying a different backend.
C bindings
~~~~~~~~~~
-When binding C code with ``cffi`` we have our own style guide, it's pretty
-simple.
-
-Don't name parameters:
-
-.. code-block:: c
-
- // Good
- long f(long);
- // Bad
- long f(long x);
-
-...unless they're inside a struct:
-
-.. code-block:: c
-
- struct my_struct {
- char *name;
- int number;
- ...;
- };
-
-Include ``void`` if the function takes no arguments:
-
-.. code-block:: c
-
- // Good
- long f(void);
- // Bad
- long f();
-
-Wrap lines at 80 characters like so:
-
-.. code-block:: c
-
- // Pretend this went to 80 characters
- long f(long, long,
- int *)
-
-Include a space after commas between parameters:
-
-.. code-block:: c
-
- // Good
- long f(int, char *)
- // Bad
- long f(int,char *)
-
-Values set by ``#define`` should be assigned the appropriate type. If you see
-this:
-
-.. code-block:: c
-
- #define SOME_INTEGER_LITERAL 0x0;
- #define SOME_UNSIGNED_INTEGER_LITERAL 0x0001U;
- #define SOME_STRING_LITERAL "hello";
-
-...it should be added to the bindings like so:
-
-.. code-block:: c
-
- static const int SOME_INTEGER_LITERAL;
- static const unsigned int SOME_UNSIGNED_INTEGER_LITERAL;
- static const char *const SOME_STRING_LITERAL;
+More information on C bindings can be found in :doc:`the dedicated
+section of the documentation <c-bindings>`.
Tests
-----
diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst
index 6848d84c..2167e528 100644
--- a/docs/hazmat/primitives/asymmetric/dsa.rst
+++ b/docs/hazmat/primitives/asymmetric/dsa.rst
@@ -210,6 +210,73 @@ DSA
:returns:
:class:`~cryptography.hazmat.primitives.interfaces.AsymmetricVerificationContext`
+.. class:: DSAParameterNumbers(p, q, g)
+
+ .. versionadded:: 0.5
+
+ The collection of integers that make up a set of DSA parameters.
+
+ .. attribute:: p
+
+ :type: int
+
+ The public modulus.
+
+ .. attribute:: q
+
+ :type: int
+
+ The sub-group order.
+
+ .. attribute:: g
+
+ :type: int
+
+ The generator.
+
+.. class:: DSAPublicNumbers(y, parameter_numbers)
+
+ .. versionadded:: 0.5
+
+ The collection of integers that make up a DSA public key.
+
+ .. attribute:: y
+
+ :type: int
+
+ The public value ``y``.
+
+ .. attribute:: parameter_numbers
+
+ :type: :class:`~cryptography.hazmat.primitives.dsa.DSAParameterNumbers`
+
+ The :class:`~cryptography.hazmat.primitives.dsa.DSAParameterNumbers`
+ associated with the public key.
+
+.. class:: DSAPrivateNumbers(x, public_numbers)
+
+ .. versionadded:: 0.5
+
+ The collection of integers that make up a DSA private key.
+
+ .. warning::
+
+ Revealing the value of ``x`` will compromise the security of any
+ cryptographic operations performed.
+
+ .. attribute:: x
+
+ :type: int
+
+ The private value ``x``.
+
+ .. attribute:: public_numbers
+
+ :type: :class:`~cryptography.hazmat.primitives.dsa.DSAPublicNumbers`
+
+ The :class:`~cryptography.hazmat.primitives.dsa.DSAPublicNumbers`
+ associated with the private key.
+
.. _`DSA`: https://en.wikipedia.org/wiki/Digital_Signature_Algorithm
.. _`public-key`: https://en.wikipedia.org/wiki/Public-key_cryptography
.. _`FIPS 186-4`: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
diff --git a/docs/hazmat/primitives/interfaces.rst b/docs/hazmat/primitives/interfaces.rst
index b8aca0c2..0c456326 100644
--- a/docs/hazmat/primitives/interfaces.rst
+++ b/docs/hazmat/primitives/interfaces.rst
@@ -13,7 +13,7 @@ to document argument and return types.
Symmetric ciphers
-~~~~~~~~~~~~~~~~~
+-----------------
.. currentmodule:: cryptography.hazmat.primitives.interfaces
@@ -48,7 +48,7 @@ Symmetric ciphers
Cipher modes
-------------
+~~~~~~~~~~~~
Interfaces used by the symmetric cipher modes described in
:ref:`Symmetric Encryption Modes <symmetric-encryption-modes>`.
@@ -104,7 +104,44 @@ Interfaces used by the symmetric cipher modes described in
individual modes.
Asymmetric interfaces
-~~~~~~~~~~~~~~~~~~~~~
+---------------------
+
+.. class:: AsymmetricSignatureContext
+
+ .. versionadded:: 0.2
+
+ .. method:: update(data)
+
+ :param bytes data: The data you want to sign.
+
+ .. method:: finalize()
+
+ :return bytes signature: The signature.
+
+
+.. class:: AsymmetricVerificationContext
+
+ .. versionadded:: 0.2
+
+ .. method:: update(data)
+
+ :param bytes data: The data you wish to verify using the signature.
+
+ .. method:: verify()
+
+ :raises cryptography.exceptions.InvalidSignature: If the signature does
+ not validate.
+
+
+.. class:: AsymmetricPadding
+
+ .. versionadded:: 0.2
+
+ .. attribute:: name
+
+
+RSA
+~~~
.. class:: RSAPrivateKey
@@ -236,6 +273,9 @@ Asymmetric interfaces
instance.
+DSA
+~~~
+
.. class:: DSAParameters
.. versionadded:: 0.3
@@ -458,6 +498,9 @@ Asymmetric interfaces
The bit length of the curve's base point.
+Elliptic Curve
+~~~~~~~~~~~~~~
+
.. class:: EllipticCurveSignatureAlgorithm
.. versionadded:: 0.5
@@ -526,42 +569,8 @@ Asymmetric interfaces
The elliptic curve for this key.
-.. class:: AsymmetricSignatureContext
-
- .. versionadded:: 0.2
-
- .. method:: update(data)
-
- :param bytes data: The data you want to sign.
-
- .. method:: finalize()
-
- :return bytes signature: The signature.
-
-
-.. class:: AsymmetricVerificationContext
-
- .. versionadded:: 0.2
-
- .. method:: update(data)
-
- :param bytes data: The data you wish to verify using the signature.
-
- .. method:: verify()
-
- :raises cryptography.exceptions.InvalidSignature: If the signature does
- not validate.
-
-
-.. class:: AsymmetricPadding
-
- .. versionadded:: 0.2
-
- .. attribute:: name
-
-
Hash algorithms
-~~~~~~~~~~~~~~~
+---------------
.. class:: HashAlgorithm
@@ -607,7 +616,7 @@ Hash algorithms
Key derivation functions
-~~~~~~~~~~~~~~~~~~~~~~~~
+------------------------
.. class:: KeyDerivationFunction
@@ -652,7 +661,7 @@ Key derivation functions
`CMAC`_
-~~~~~~~
+-------
.. class:: CMACContext
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index e7a63f26..dc123493 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -30,8 +30,12 @@ invariants
iOS
Koblitz
metadata
+namespace
+namespaces
pickleable
plaintext
+preprocessor
+preprocessors
pseudorandom
Schneier
scrypt
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index 0dd91695..bd99c8f2 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -487,11 +487,6 @@ class TestOpenSSLNoEllipticCurve(object):
None, None
) is False
- def test_supported_curves(self, monkeypatch):
- monkeypatch.setattr(backend._lib, "Cryptography_HAS_EC", 0)
-
- assert backend._supported_curves() == []
-
class TestDeprecatedRSABackendMethods(object):
def test_create_rsa_signature_ctx(self):
diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py
index cbe10e9c..51a03c48 100644
--- a/tests/hazmat/primitives/test_dsa.py
+++ b/tests/hazmat/primitives/test_dsa.py
@@ -872,3 +872,63 @@ def test_dsa_generate_invalid_backend():
with raises_unsupported_algorithm(
_Reasons.BACKEND_MISSING_INTERFACE):
dsa.DSAPrivateKey.generate(pretend_parameters, pretend_backend)
+
+
+class TestDSANumbers(object):
+ def test_dsa_parameter_numbers(self):
+ parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
+ assert parameter_numbers.p == 1
+ assert parameter_numbers.q == 2
+ assert parameter_numbers.g == 3
+
+ def test_dsa_parameter_numbers_invalid_types(self):
+ with pytest.raises(TypeError):
+ dsa.DSAParameterNumbers(p=None, q=2, g=3)
+
+ with pytest.raises(TypeError):
+ dsa.DSAParameterNumbers(p=1, q=None, g=3)
+
+ with pytest.raises(TypeError):
+ dsa.DSAParameterNumbers(p=1, q=2, g=None)
+
+ def test_dsa_public_numbers(self):
+ parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
+ public_numbers = dsa.DSAPublicNumbers(
+ y=4,
+ parameter_numbers=parameter_numbers
+ )
+ assert public_numbers.y == 4
+ assert public_numbers.parameter_numbers == parameter_numbers
+
+ def test_dsa_public_numbers_invalid_types(self):
+ with pytest.raises(TypeError):
+ dsa.DSAPublicNumbers(y=4, parameter_numbers=None)
+
+ with pytest.raises(TypeError):
+ parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
+ dsa.DSAPublicNumbers(y=None, parameter_numbers=parameter_numbers)
+
+ def test_dsa_private_numbers(self):
+ parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
+ public_numbers = dsa.DSAPublicNumbers(
+ y=4,
+ parameter_numbers=parameter_numbers
+ )
+ private_numbers = dsa.DSAPrivateNumbers(
+ x=5,
+ public_numbers=public_numbers
+ )
+ assert private_numbers.x == 5
+ assert private_numbers.public_numbers == public_numbers
+
+ def test_dsa_private_numbers_invalid_types(self):
+ parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
+ public_numbers = dsa.DSAPublicNumbers(
+ y=4,
+ parameter_numbers=parameter_numbers
+ )
+ with pytest.raises(TypeError):
+ dsa.DSAPrivateNumbers(x=4, public_numbers=None)
+
+ with pytest.raises(TypeError):
+ dsa.DSAPrivateNumbers(x=None, public_numbers=public_numbers)
diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py
index 1879f4fc..2690e794 100644
--- a/tests/hazmat/primitives/test_ec.py
+++ b/tests/hazmat/primitives/test_ec.py
@@ -70,6 +70,15 @@ def _skip_ecdsa_vector(backend, curve_type, hash_type):
)
+def _skip_curve_unsupported(backend, curve):
+ if not backend.elliptic_curve_supported(curve):
+ pytest.skip(
+ "Curve {0} is not supported by this backend {1}".format(
+ curve.name, backend
+ )
+ )
+
+
@utils.register_interface(interfaces.EllipticCurve)
class DummyCurve(object):
name = "dummy-curve"
@@ -81,6 +90,12 @@ class DummySignatureAlgorithm(object):
pass
+@pytest.mark.elliptic
+def test_skip_curve_unsupported(backend):
+ with pytest.raises(pytest.skip.Exception):
+ _skip_curve_unsupported(backend, DummyCurve())
+
+
def test_ec_numbers():
numbers = ec.EllipticCurvePrivateNumbers(
1,
@@ -176,12 +191,7 @@ class TestECDSAVectors(object):
"curve", _CURVE_TYPES.values()
)
def test_generate_vector_curves(self, backend, curve):
- if not backend.elliptic_curve_supported(curve()):
- pytest.skip(
- "Curve {0} is not supported by this backend {1}".format(
- curve().name, backend
- )
- )
+ _skip_curve_unsupported(backend, curve())
key = ec.generate_private_key(curve(), backend)
assert key
@@ -205,12 +215,7 @@ class TestECDSAVectors(object):
) is False
def test_unknown_signature_algoritm(self, backend):
- if not backend.elliptic_curve_supported(ec.SECP192R1()):
- pytest.skip(
- "Curve secp192r1 is not supported by this backend {0}".format(
- backend
- )
- )
+ _skip_curve_unsupported(backend, ec.SECP192R1())
key = ec.generate_private_key(ec.SECP192R1(), backend)