diff options
| author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2014-06-07 13:55:53 -0500 |
|---|---|---|
| committer | Paul Kehrer <paul.l.kehrer@gmail.com> | 2014-06-07 13:55:53 -0500 |
| commit | 425e5b04bb18ce2e563d891f3502365e3b9c071a (patch) | |
| tree | 6fe6155fca2d6943f7615ea391e43538d6394c38 /cryptography | |
| parent | ddadf40234e97cd5b7e5f7b3a3a03d38900cb291 (diff) | |
| parent | e47bafb9b620b557aeb48fce4734a568d6dc0b38 (diff) | |
| download | cryptography-425e5b04bb18ce2e563d891f3502365e3b9c071a.tar.gz cryptography-425e5b04bb18ce2e563d891f3502365e3b9c071a.tar.bz2 cryptography-425e5b04bb18ce2e563d891f3502365e3b9c071a.zip | |
Merge pull request #1090 from public/numberless-ecdsa-backend
Numberless ECDSA backend
Diffstat (limited to 'cryptography')
| -rw-r--r-- | cryptography/exceptions.py | 1 | ||||
| -rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 398 | ||||
| -rw-r--r-- | cryptography/hazmat/primitives/asymmetric/ec.py | 186 |
3 files changed, 579 insertions, 6 deletions
diff --git a/cryptography/exceptions.py b/cryptography/exceptions.py index b4ee8feb..c64b67f4 100644 --- a/cryptography/exceptions.py +++ b/cryptography/exceptions.py @@ -21,6 +21,7 @@ class _Reasons(object): UNSUPPORTED_PADDING = object() UNSUPPORTED_MGF = object() UNSUPPORTED_PUBLIC_KEY_ALGORITHM = object() + UNSUPPORTED_ELLIPTIC_CURVE = object() class UnsupportedAlgorithm(Exception): diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index ffe09663..9cf92f9b 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -25,13 +25,13 @@ from cryptography.exceptions import ( UnsupportedAlgorithm, _Reasons ) from cryptography.hazmat.backends.interfaces import ( - CMACBackend, CipherBackend, DSABackend, HMACBackend, HashBackend, - PBKDF2HMACBackend, PKCS8SerializationBackend, RSABackend, + CMACBackend, CipherBackend, DSABackend, EllipticCurveBackend, HMACBackend, + HashBackend, PBKDF2HMACBackend, PKCS8SerializationBackend, RSABackend, TraditionalOpenSSLSerializationBackend ) from cryptography.hazmat.bindings.openssl.binding import Binding from cryptography.hazmat.primitives import hashes, interfaces -from cryptography.hazmat.primitives.asymmetric import dsa, rsa +from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa from cryptography.hazmat.primitives.asymmetric.padding import ( MGF1, OAEP, PKCS1v15, PSS ) @@ -51,12 +51,13 @@ _OpenSSLError = collections.namedtuple("_OpenSSLError", @utils.register_interface(CipherBackend) @utils.register_interface(CMACBackend) @utils.register_interface(DSABackend) +@utils.register_interface(EllipticCurveBackend) @utils.register_interface(HashBackend) @utils.register_interface(HMACBackend) @utils.register_interface(PBKDF2HMACBackend) +@utils.register_interface(PKCS8SerializationBackend) @utils.register_interface(RSABackend) @utils.register_interface(TraditionalOpenSSLSerializationBackend) -@utils.register_interface(PKCS8SerializationBackend) class Backend(object): """ OpenSSL API binding interfaces. @@ -425,8 +426,8 @@ class Backend(object): The char* is the storage for the BIO and it must stay alive until the BIO is finished with. """ - data_char_p = backend._ffi.new("char[]", data) - bio = backend._lib.BIO_new_mem_buf( + data_char_p = self._ffi.new("char[]", data) + bio = self._lib.BIO_new_mem_buf( data_char_p, len(data) ) assert bio != self._ffi.NULL @@ -890,6 +891,239 @@ class Backend(object): return self._evp_pkey_to_private_key(evp_pkey) + def elliptic_curve_supported(self, curve): + if self._lib.Cryptography_HAS_EC != 1: + return False + + curves = self._supported_curves() + return curve.name.encode("ascii") in curves + + def elliptic_curve_signature_algorithm_supported( + self, signature_algorithm, curve + ): + if self._lib.Cryptography_HAS_EC != 1: + return False + + # We only support ECDSA right now. + if isinstance(signature_algorithm, ec.ECDSA) is False: + return False + + # Before 0.9.8m OpenSSL can't cope with digests longer than the curve. + if ( + self._lib.OPENSSL_VERSION_NUMBER < 0x009080df and + curve.key_size < signature_algorithm.algorithm.digest_size * 8 + ): + return False + + if not self.elliptic_curve_supported(curve): + return False + else: + return True + + 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) + + def _create_ecdsa_verification_ctx(self, public_key, signature, ecdsa): + return _ECDSAVerificationContext(self, public_key, signature, + ecdsa.algorithm) + + def generate_elliptic_curve_private_key(self, curve): + """ + Generate a new private key on the named curve. + """ + + curve_nid = self._elliptic_curve_to_nid(curve) + + ctx = self._lib.EC_KEY_new_by_curve_name(curve_nid) + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.EC_KEY_free) + + res = self._lib.EC_KEY_generate_key(ctx) + assert res == 1 + + res = self._lib.EC_KEY_check_key(ctx) + assert res == 1 + + return _EllipticCurvePrivateKey(self, ctx, curve) + + def elliptic_curve_private_key_from_numbers(self, numbers): + ec_key = self._ec_key_cdata_from_private_numbers(numbers) + return _EllipticCurvePrivateKey(self, ec_key, + numbers.public_numbers.curve) + + def elliptic_curve_public_key_from_numbers(self, numbers): + ec_key = self._ec_key_cdata_from_public_numbers(numbers) + return _EllipticCurvePublicKey(self, ec_key, numbers.curve) + + def _elliptic_curve_to_nid(self, curve): + """ + Get the NID for a curve name. + """ + + curve_aliases = { + "secp192r1": "prime192v1", + "secp256r1": "prime256v1" + } + + curve_name = curve_aliases.get(curve.name, curve.name) + + curve_nid = self._lib.OBJ_sn2nid(curve_name.encode()) + if curve_nid == self._lib.NID_undef: + raise UnsupportedAlgorithm( + "{0} is not a supported elliptic curve".format(curve.name), + _Reasons.UNSUPPORTED_ELLIPTIC_CURVE + ) + return curve_nid + + def _ec_key_cdata_from_private_numbers(self, numbers): + """ + Build an EC_KEY from a private key object. + """ + + public = numbers.public_numbers + + curve_nid = self._elliptic_curve_to_nid(public.curve) + + ctx = self._lib.EC_KEY_new_by_curve_name(curve_nid) + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.EC_KEY_free) + + ctx = self._ec_key_set_public_key_affine_coordinates( + ctx, public.x, public.y) + + res = self._lib.EC_KEY_set_private_key( + ctx, self._int_to_bn(numbers.private_value)) + assert res == 1 + + return ctx + + def _ec_key_cdata_from_public_numbers(self, numbers): + """ + Build an EC_KEY from a public key object. + """ + + curve_nid = self._elliptic_curve_to_nid(numbers.curve) + + ctx = self._lib.EC_KEY_new_by_curve_name(curve_nid) + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.EC_KEY_free) + + ctx = self._ec_key_set_public_key_affine_coordinates( + ctx, numbers.x, numbers.y) + + return ctx + + def _public_ec_key_from_private_ec_key(self, private_key_cdata): + """ + Copy the public portions out of one EC key into a new one. + """ + + group = self._lib.EC_KEY_get0_group(private_key_cdata) + assert group != self._ffi.NULL + + curve_nid = self._lib.EC_GROUP_get_curve_name(group) + + ctx = self._lib.EC_KEY_new_by_curve_name(curve_nid) + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.EC_KEY_free) + + point = self._lib.EC_KEY_get0_public_key(private_key_cdata) + assert point != self._ffi.NULL + + res = self._lib.EC_KEY_set_public_key(ctx, point) + assert res == 1 + + return ctx + + def _ec_key_set_public_key_affine_coordinates(self, ctx, x, y): + """ + This is a port of EC_KEY_set_public_key_affine_coordinates that was + added in 1.0.1. + + Sets the public key point in the EC_KEY context to the affine x and y + values. + """ + + assert ctx != self._ffi.NULL + + bn_x = self._int_to_bn(x) + bn_y = self._int_to_bn(y) + + nid_two_field = self._lib.OBJ_sn2nid(b"characteristic-two-field") + assert nid_two_field != self._lib.NID_undef + + bn_ctx = self._lib.BN_CTX_new() + assert bn_ctx != self._ffi.NULL + bn_ctx = self._ffi.gc(bn_ctx, self._lib.BN_CTX_free) + + group = self._lib.EC_KEY_get0_group(ctx) + assert group != self._ffi.NULL + + point = self._lib.EC_POINT_new(group) + assert point != self._ffi.NULL + point = self._ffi.gc(point, self._lib.EC_POINT_free) + + method = self._lib.EC_GROUP_method_of(group) + assert method != self._ffi.NULL + + nid = self._lib.EC_METHOD_get_field_type(method) + assert nid != self._lib.NID_undef + + check_x = self._lib.BN_CTX_get(bn_ctx) + check_y = self._lib.BN_CTX_get(bn_ctx) + + if nid == nid_two_field and self._lib.Cryptography_HAS_EC2M: + set_func = self._lib.EC_POINT_set_affine_coordinates_GF2m + get_func = self._lib.EC_POINT_get_affine_coordinates_GF2m + else: + set_func = self._lib.EC_POINT_set_affine_coordinates_GFp + get_func = self._lib.EC_POINT_get_affine_coordinates_GFp + + assert set_func and get_func + + res = set_func(group, point, bn_x, bn_y, bn_ctx) + assert res == 1 + + res = get_func(group, point, check_x, check_y, bn_ctx) + assert res == 1 + + assert ( + self._lib.BN_cmp(bn_x, check_x) == 0 and + self._lib.BN_cmp(bn_y, check_y) == 0 + ) + + res = self._lib.EC_KEY_set_public_key(ctx, point) + assert res == 1 + + res = self._lib.EC_KEY_check_key(ctx) + assert res == 1 + + return ctx + class GetCipherByName(object): def __init__(self, fmt): @@ -1727,4 +1961,156 @@ 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) + + backend = Backend() diff --git a/cryptography/hazmat/primitives/asymmetric/ec.py b/cryptography/hazmat/primitives/asymmetric/ec.py index 1e49ad7b..220a419c 100644 --- a/cryptography/hazmat/primitives/asymmetric/ec.py +++ b/cryptography/hazmat/primitives/asymmetric/ec.py @@ -15,9 +15,189 @@ from __future__ import absolute_import, division, print_function import six +from cryptography import utils from cryptography.hazmat.primitives import interfaces +@utils.register_interface(interfaces.EllipticCurve) +class SECT571R1(object): + @property + def name(self): + return "sect571r1" + + @property + def key_size(self): + return 571 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT409R1(object): + @property + def name(self): + return "sect409r1" + + @property + def key_size(self): + return 409 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT283R1(object): + @property + def name(self): + return "sect283r1" + + @property + def key_size(self): + return 283 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT233R1(object): + @property + def name(self): + return "sect233r1" + + @property + def key_size(self): + return 233 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT163R2(object): + @property + def name(self): + return "sect163r2" + + @property + def key_size(self): + return 163 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT571K1(object): + @property + def name(self): + return "sect571k1" + + @property + def key_size(self): + return 571 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT409K1(object): + @property + def name(self): + return "sect409k1" + + @property + def key_size(self): + return 409 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT283K1(object): + @property + def name(self): + return "sect283k1" + + @property + def key_size(self): + return 283 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT233K1(object): + @property + def name(self): + return "sect233k1" + + @property + def key_size(self): + return 233 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECT163K1(object): + @property + def name(self): + return "sect163k1" + + @property + def key_size(self): + return 163 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECP521R1(object): + @property + def name(self): + return "secp521r1" + + @property + def key_size(self): + return 521 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECP384R1(object): + @property + def name(self): + return "secp384r1" + + @property + def key_size(self): + return 384 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECP256R1(object): + @property + def name(self): + return "secp256r1" + + @property + def key_size(self): + return 256 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECP224R1(object): + @property + def name(self): + return "secp224r1" + + @property + def key_size(self): + return 224 + + +@utils.register_interface(interfaces.EllipticCurve) +class SECP192R1(object): + @property + def name(self): + return "secp192r1" + + @property + def key_size(self): + return 192 + + +@utils.register_interface(interfaces.EllipticCurveSignatureAlgorithm) +class ECDSA(object): + def __init__(self, algorithm): + self._algorithm = algorithm + + @property + def algorithm(self): + return self._algorithm + + +def generate_private_key(curve, backend): + return backend.generate_elliptic_curve_private_key(curve) + + class EllipticCurvePublicNumbers(object): def __init__(self, x, y, curve): if ( @@ -33,6 +213,9 @@ class EllipticCurvePublicNumbers(object): self._x = x self._curve = curve + def public_key(self, backend): + return backend.elliptic_curve_public_key_from_numbers(self) + @property def curve(self): return self._curve @@ -60,6 +243,9 @@ class EllipticCurvePrivateNumbers(object): self._private_value = private_value self._public_numbers = public_numbers + def private_key(self, backend): + return backend.elliptic_curve_private_key_from_numbers(self) + @property def private_value(self): return self._private_value |
