diff options
| author | David Reid <dreid@dreid.org> | 2014-06-27 14:25:25 -0700 |
|---|---|---|
| committer | David Reid <dreid@dreid.org> | 2014-06-27 14:25:25 -0700 |
| commit | 0e2ee9b15b78fb2e7e08643b1adcdabb63f441e3 (patch) | |
| tree | f69429233cc61a5962e37b997944e1439dffca31 /cryptography | |
| parent | 9371dbfbaa61b17b6996c6fe43ee426e3092af04 (diff) | |
| parent | 298effd54c11ed47077f580f74d9204f7acce3f5 (diff) | |
| download | cryptography-0e2ee9b15b78fb2e7e08643b1adcdabb63f441e3.tar.gz cryptography-0e2ee9b15b78fb2e7e08643b1adcdabb63f441e3.tar.bz2 cryptography-0e2ee9b15b78fb2e7e08643b1adcdabb63f441e3.zip | |
Merge pull request #1179 from reaperhulk/dsa-numbers-opaque-backend
DSA Opaque Keys for OpenSSL
Diffstat (limited to 'cryptography')
| -rw-r--r-- | cryptography/hazmat/backends/interfaces.py | 10 | ||||
| -rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 97 | ||||
| -rw-r--r-- | cryptography/hazmat/backends/openssl/dsa.py | 124 | ||||
| -rw-r--r-- | cryptography/hazmat/primitives/asymmetric/dsa.py | 99 | ||||
| -rw-r--r-- | cryptography/hazmat/primitives/interfaces.py | 6 |
5 files changed, 268 insertions, 68 deletions
diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py index e4faf32c..5ed49966 100644 --- a/cryptography/hazmat/backends/interfaces.py +++ b/cryptography/hazmat/backends/interfaces.py @@ -166,8 +166,14 @@ class DSABackend(object): @abc.abstractmethod def generate_dsa_private_key(self, parameters): """ - Generate an DSAPrivateKey instance with parameters as - a DSAParameters object. + Generate a DSAPrivateKey instance with parameters as a DSAParameters + object. + """ + + @abc.abstractmethod + def generate_dsa_private_key_and_parameters(self, key_size): + """ + Generate a DSAPrivateKey instance using key size only. """ @abc.abstractmethod diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 5776ea6f..b3396ea9 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -33,6 +33,7 @@ from cryptography.hazmat.backends.openssl.ciphers import ( ) from cryptography.hazmat.backends.openssl.cmac import _CMACContext from cryptography.hazmat.backends.openssl.dsa import ( + _DSAParameters, _DSAPrivateKey, _DSAPublicKey, _DSASignatureContext, _DSAVerificationContext ) from cryptography.hazmat.backends.openssl.ec import ( @@ -470,19 +471,10 @@ class Backend(object): dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) assert dsa_cdata != self._ffi.NULL dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - return self._dsa_cdata_to_private_key(dsa_cdata) + return _DSAPrivateKey(self, dsa_cdata) else: raise UnsupportedAlgorithm("Unsupported key type.") - def _dsa_cdata_to_private_key(self, cdata): - return dsa.DSAPrivateKey( - modulus=self._bn_to_int(cdata.p), - subgroup_order=self._bn_to_int(cdata.q), - generator=self._bn_to_int(cdata.g), - x=self._bn_to_int(cdata.priv_key), - y=self._bn_to_int(cdata.pub_key) - ) - def _pem_password_cb(self, password): """ Generate a pem_password_cb function pointer that copied the password to @@ -618,43 +610,85 @@ class Backend(object): assert res == 1 - return dsa.DSAParameters( - modulus=self._bn_to_int(ctx.p), - subgroup_order=self._bn_to_int(ctx.q), - generator=self._bn_to_int(ctx.g) - ) + return _DSAParameters(self, ctx) def generate_dsa_private_key(self, parameters): ctx = self._lib.DSA_new() assert ctx != self._ffi.NULL ctx = self._ffi.gc(ctx, self._lib.DSA_free) - ctx.p = self._int_to_bn(parameters.p) - ctx.q = self._int_to_bn(parameters.q) - ctx.g = self._int_to_bn(parameters.g) + if isinstance(parameters, dsa.DSAParameters): + ctx.p = self._int_to_bn(parameters.p) + ctx.q = self._int_to_bn(parameters.q) + ctx.g = self._int_to_bn(parameters.g) + else: + ctx.p = self._lib.BN_dup(parameters._dsa_cdata.p) + ctx.q = self._lib.BN_dup(parameters._dsa_cdata.q) + ctx.g = self._lib.BN_dup(parameters._dsa_cdata.g) self._lib.DSA_generate_key(ctx) - return dsa.DSAPrivateKey( - modulus=self._bn_to_int(ctx.p), - subgroup_order=self._bn_to_int(ctx.q), - generator=self._bn_to_int(ctx.g), - x=self._bn_to_int(ctx.priv_key), - y=self._bn_to_int(ctx.pub_key) - ) + return _DSAPrivateKey(self, ctx) + + def generate_dsa_private_key_and_parameters(self, key_size): + parameters = self.generate_dsa_parameters(key_size) + return self.generate_dsa_private_key(parameters) def create_dsa_signature_ctx(self, private_key, algorithm): - return _DSASignatureContext(self, private_key, algorithm) + dsa_cdata = self._dsa_cdata_from_private_key(private_key) + key = _DSAPrivateKey(self, dsa_cdata) + return _DSASignatureContext(self, key, algorithm) def create_dsa_verification_ctx(self, public_key, signature, algorithm): - return _DSAVerificationContext(self, public_key, signature, - algorithm) + dsa_cdata = self._dsa_cdata_from_public_key(public_key) + key = _DSAPublicKey(self, dsa_cdata) + return _DSAVerificationContext(self, key, signature, algorithm) + + def load_dsa_private_numbers(self, numbers): + dsa._check_dsa_private_numbers(numbers) + parameter_numbers = numbers.public_numbers.parameter_numbers + + dsa_cdata = self._lib.DSA_new() + assert dsa_cdata != self._ffi.NULL + dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + + dsa_cdata.p = self._int_to_bn(parameter_numbers.p) + dsa_cdata.q = self._int_to_bn(parameter_numbers.q) + dsa_cdata.g = self._int_to_bn(parameter_numbers.g) + dsa_cdata.pub_key = self._int_to_bn(numbers.public_numbers.y) + dsa_cdata.priv_key = self._int_to_bn(numbers.x) + + return _DSAPrivateKey(self, dsa_cdata) + + def load_dsa_public_numbers(self, numbers): + dsa._check_dsa_parameters(numbers.parameter_numbers) + dsa_cdata = self._lib.DSA_new() + assert dsa_cdata != self._ffi.NULL + dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + + dsa_cdata.p = self._int_to_bn(numbers.parameter_numbers.p) + dsa_cdata.q = self._int_to_bn(numbers.parameter_numbers.q) + dsa_cdata.g = self._int_to_bn(numbers.parameter_numbers.g) + dsa_cdata.pub_key = self._int_to_bn(numbers.y) + + return _DSAPublicKey(self, dsa_cdata) + + def load_dsa_parameter_numbers(self, numbers): + dsa._check_dsa_parameters(numbers) + dsa_cdata = self._lib.DSA_new() + assert dsa_cdata != self._ffi.NULL + dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + + dsa_cdata.p = self._int_to_bn(numbers.p) + dsa_cdata.q = self._int_to_bn(numbers.q) + dsa_cdata.g = self._int_to_bn(numbers.g) + + return _DSAParameters(self, dsa_cdata) def _dsa_cdata_from_public_key(self, public_key): - # Does not GC the DSA cdata. You *must* make sure it's freed - # correctly yourself! ctx = self._lib.DSA_new() assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.DSA_free) parameters = public_key.parameters() ctx.p = self._int_to_bn(parameters.p) ctx.q = self._int_to_bn(parameters.q) @@ -663,10 +697,9 @@ class Backend(object): return ctx def _dsa_cdata_from_private_key(self, private_key): - # Does not GC the DSA cdata. You *must* make sure it's freed - # correctly yourself! ctx = self._lib.DSA_new() assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.DSA_free) parameters = private_key.parameters() ctx.p = self._int_to_bn(parameters.p) ctx.q = self._int_to_bn(parameters.q) diff --git a/cryptography/hazmat/backends/openssl/dsa.py b/cryptography/hazmat/backends/openssl/dsa.py index ec05c3aa..5e7a26ff 100644 --- a/cryptography/hazmat/backends/openssl/dsa.py +++ b/cryptography/hazmat/backends/openssl/dsa.py @@ -16,6 +16,10 @@ from __future__ import absolute_import, division, print_function from cryptography import utils from cryptography.exceptions import InvalidSignature from cryptography.hazmat.primitives import hashes, interfaces +from cryptography.hazmat.primitives.asymmetric import dsa +from cryptography.hazmat.primitives.interfaces import ( + DSAParametersWithNumbers, DSAPrivateKeyWithNumbers, DSAPublicKeyWithNumbers +) @utils.register_interface(interfaces.AsymmetricVerificationContext) @@ -32,9 +36,7 @@ class _DSAVerificationContext(object): self._hash_ctx.update(data) def verify(self): - self._dsa_cdata = self._backend._dsa_cdata_from_public_key( - self._public_key) - self._dsa_cdata = self._backend._ffi.gc(self._dsa_cdata, + self._dsa_cdata = self._backend._ffi.gc(self._public_key._dsa_cdata, self._backend._lib.DSA_free) data_to_verify = self._hash_ctx.finalize() @@ -43,7 +45,7 @@ class _DSAVerificationContext(object): # must be an integer. res = self._backend._lib.DSA_verify( 0, data_to_verify, len(data_to_verify), self._signature, - len(self._signature), self._dsa_cdata) + len(self._signature), self._public_key._dsa_cdata) if res != 1: errors = self._backend._consume_errors() @@ -61,17 +63,13 @@ class _DSASignatureContext(object): self._private_key = private_key self._algorithm = algorithm self._hash_ctx = hashes.Hash(self._algorithm, self._backend) - self._dsa_cdata = self._backend._dsa_cdata_from_private_key( - self._private_key) - self._dsa_cdata = self._backend._ffi.gc(self._dsa_cdata, - self._backend._lib.DSA_free) def update(self, data): self._hash_ctx.update(data) def finalize(self): data_to_sign = self._hash_ctx.finalize() - sig_buf_len = self._backend._lib.DSA_size(self._dsa_cdata) + sig_buf_len = self._backend._lib.DSA_size(self._private_key._dsa_cdata) sig_buf = self._backend._ffi.new("unsigned char[]", sig_buf_len) buflen = self._backend._ffi.new("unsigned int *") @@ -79,8 +77,114 @@ class _DSASignatureContext(object): # must be an integer. res = self._backend._lib.DSA_sign( 0, data_to_sign, len(data_to_sign), sig_buf, - buflen, self._dsa_cdata) + buflen, self._private_key._dsa_cdata) assert res == 1 assert buflen[0] return self._backend._ffi.buffer(sig_buf)[:buflen[0]] + + +@utils.register_interface(DSAParametersWithNumbers) +class _DSAParameters(object): + def __init__(self, backend, dsa_cdata): + self._backend = backend + self._dsa_cdata = dsa_cdata + + def parameter_numbers(self): + return dsa.DSAParameterNumbers( + p=self._backend._bn_to_int(self._dsa_cdata.p), + q=self._backend._bn_to_int(self._dsa_cdata.q), + g=self._backend._bn_to_int(self._dsa_cdata.g) + ) + + def generate_private_key(self): + return self._backend.generate_dsa_private_key(self) + + +@utils.register_interface(DSAPrivateKeyWithNumbers) +class _DSAPrivateKey(object): + def __init__(self, backend, dsa_cdata): + self._backend = backend + self._dsa_cdata = dsa_cdata + self._key_size = self._backend._lib.BN_num_bits(self._dsa_cdata.p) + + @property + def key_size(self): + return self._key_size + + def signer(self, algorithm): + return _DSASignatureContext(self._backend, self, algorithm) + + def private_numbers(self): + return dsa.DSAPrivateNumbers( + public_numbers=dsa.DSAPublicNumbers( + parameter_numbers=dsa.DSAParameterNumbers( + p=self._backend._bn_to_int(self._dsa_cdata.p), + q=self._backend._bn_to_int(self._dsa_cdata.q), + g=self._backend._bn_to_int(self._dsa_cdata.g) + ), + y=self._backend._bn_to_int(self._dsa_cdata.pub_key) + ), + x=self._backend._bn_to_int(self._dsa_cdata.priv_key) + ) + + def public_key(self): + dsa_cdata = self._backend._lib.DSA_new() + assert dsa_cdata != self._backend._ffi.NULL + dsa_cdata = self._backend._ffi.gc( + dsa_cdata, self._backend._lib.DSA_free + ) + dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p) + dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q) + dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g) + dsa_cdata.pub_key = self._backend._lib.BN_dup(self._dsa_cdata.pub_key) + return _DSAPublicKey(self._backend, dsa_cdata) + + def parameters(self): + dsa_cdata = self._backend._lib.DSA_new() + assert dsa_cdata != self._backend._ffi.NULL + dsa_cdata = self._backend._ffi.gc( + dsa_cdata, self._backend._lib.DSA_free + ) + dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p) + dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q) + dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g) + return _DSAParameters(self._backend, dsa_cdata) + + +@utils.register_interface(DSAPublicKeyWithNumbers) +class _DSAPublicKey(object): + def __init__(self, backend, dsa_cdata): + self._backend = backend + self._dsa_cdata = dsa_cdata + self._key_size = self._backend._lib.BN_num_bits(self._dsa_cdata.p) + + @property + def key_size(self): + return self._key_size + + def verifier(self, signature, algorithm): + return _DSAVerificationContext( + self._backend, self, signature, algorithm + ) + + def public_numbers(self): + return dsa.DSAPublicNumbers( + parameter_numbers=dsa.DSAParameterNumbers( + p=self._backend._bn_to_int(self._dsa_cdata.p), + q=self._backend._bn_to_int(self._dsa_cdata.q), + g=self._backend._bn_to_int(self._dsa_cdata.g) + ), + y=self._backend._bn_to_int(self._dsa_cdata.pub_key) + ) + + def parameters(self): + dsa_cdata = self._backend._lib.DSA_new() + assert dsa_cdata != self._backend._ffi.NULL + dsa_cdata = self._backend._ffi.gc( + dsa_cdata, self._backend._lib.DSA_free + ) + dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p) + dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q) + dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g) + return _DSAParameters(self._backend, dsa_cdata) diff --git a/cryptography/hazmat/primitives/asymmetric/dsa.py b/cryptography/hazmat/primitives/asymmetric/dsa.py index 4d78679e..7a8a61c1 100644 --- a/cryptography/hazmat/primitives/asymmetric/dsa.py +++ b/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -21,31 +21,48 @@ from cryptography.hazmat.backends.interfaces import DSABackend from cryptography.hazmat.primitives import interfaces -def _check_dsa_parameters(modulus, subgroup_order, generator): - if ( - not isinstance(modulus, six.integer_types) or - not isinstance(subgroup_order, six.integer_types) or - not isinstance(generator, six.integer_types) - ): - raise TypeError("DSA parameters must be integers.") - - if (utils.bit_length(modulus), - utils.bit_length(subgroup_order)) not in ( +def generate_parameters(key_size, backend): + return backend.generate_dsa_parameters(key_size) + + +def generate_private_key(key_size, backend): + return backend.generate_dsa_private_key_and_parameters(key_size) + + +def _check_dsa_parameters(parameters): + if (utils.bit_length(parameters.p), + utils.bit_length(parameters.q)) not in ( (1024, 160), (2048, 256), (3072, 256)): - raise ValueError("modulus and subgroup_order lengths must be " + raise ValueError("p and q lengths must be " "one of these pairs (1024, 160) or (2048, 256) " "or (3072, 256).") - if generator <= 1 or generator >= modulus: - raise ValueError("generator must be > 1 and < modulus.") + if not (1 < parameters.g < parameters.p): + raise ValueError("g, p don't satisfy 1 < g < p.") + + +def _check_dsa_private_numbers(numbers): + parameters = numbers.public_numbers.parameter_numbers + _check_dsa_parameters(parameters) + if numbers.x <= 0 or numbers.x >= parameters.q: + raise ValueError("x must be > 0 and < q.") + + if numbers.public_numbers.y != pow(parameters.g, numbers.x, parameters.p): + raise ValueError("y must be equal to (g ** x % p).") @utils.register_interface(interfaces.DSAParameters) class DSAParameters(object): def __init__(self, modulus, subgroup_order, generator): - _check_dsa_parameters(modulus, subgroup_order, generator) + _check_dsa_parameters( + DSAParameterNumbers( + p=modulus, + q=subgroup_order, + g=generator + ) + ) self._modulus = modulus self._subgroup_order = subgroup_order @@ -59,7 +76,13 @@ class DSAParameters(object): _Reasons.BACKEND_MISSING_INTERFACE ) - return backend.generate_dsa_parameters(key_size) + parameters = backend.generate_dsa_parameters(key_size) + numbers = parameters.parameter_numbers() + return cls( + modulus=numbers.p, + subgroup_order=numbers.q, + generator=numbers.g + ) @property def modulus(self): @@ -89,18 +112,25 @@ class DSAParameters(object): @utils.register_interface(interfaces.DSAPrivateKey) class DSAPrivateKey(object): def __init__(self, modulus, subgroup_order, generator, x, y): - _check_dsa_parameters(modulus, subgroup_order, generator) if ( not isinstance(x, six.integer_types) or not isinstance(y, six.integer_types) ): raise TypeError("DSAPrivateKey arguments must be integers.") - if x <= 0 or x >= subgroup_order: - raise ValueError("x must be > 0 and < subgroup_order.") - - if y != pow(generator, x, modulus): - raise ValueError("y must be equal to (generator ** x % modulus).") + _check_dsa_private_numbers( + DSAPrivateNumbers( + public_numbers=DSAPublicNumbers( + parameter_numbers=DSAParameterNumbers( + p=modulus, + q=subgroup_order, + g=generator + ), + y=y + ), + x=x + ) + ) self._modulus = modulus self._subgroup_order = subgroup_order @@ -116,7 +146,15 @@ class DSAPrivateKey(object): _Reasons.BACKEND_MISSING_INTERFACE ) - return backend.generate_dsa_private_key(parameters) + key = backend.generate_dsa_private_key(parameters) + private_numbers = key.private_numbers() + return cls( + modulus=private_numbers.public_numbers.parameter_numbers.p, + subgroup_order=private_numbers.public_numbers.parameter_numbers.q, + generator=private_numbers.public_numbers.parameter_numbers.g, + x=private_numbers.x, + y=private_numbers.public_numbers.y + ) def signer(self, algorithm, backend): if not isinstance(backend, DSABackend): @@ -151,7 +189,13 @@ class DSAPrivateKey(object): @utils.register_interface(interfaces.DSAPublicKey) class DSAPublicKey(object): def __init__(self, modulus, subgroup_order, generator, y): - _check_dsa_parameters(modulus, subgroup_order, generator) + _check_dsa_parameters( + DSAParameterNumbers( + p=modulus, + q=subgroup_order, + g=generator + ) + ) if not isinstance(y, six.integer_types): raise TypeError("y must be an integer.") @@ -210,6 +254,9 @@ class DSAParameterNumbers(object): def g(self): return self._g + def parameters(self, backend): + return backend.load_dsa_parameter_numbers(self) + class DSAPublicNumbers(object): def __init__(self, y, parameter_numbers): @@ -232,6 +279,9 @@ class DSAPublicNumbers(object): def parameter_numbers(self): return self._parameter_numbers + def public_key(self, backend): + return backend.load_dsa_public_numbers(self) + class DSAPrivateNumbers(object): def __init__(self, x, public_numbers): @@ -252,3 +302,6 @@ class DSAPrivateNumbers(object): @property def public_numbers(self): return self._public_numbers + + def private_key(self, backend): + return backend.load_dsa_private_numbers(self) diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py index ef8640c2..d60f9e0e 100644 --- a/cryptography/hazmat/primitives/interfaces.py +++ b/cryptography/hazmat/primitives/interfaces.py @@ -251,7 +251,11 @@ class RSAPublicKeyWithNumbers(RSAPublicKey): @six.add_metaclass(abc.ABCMeta) class DSAParameters(object): - pass + @abc.abstractmethod + def generate_private_key(self): + """ + Generates and returns a DSAPrivateKey. + """ @six.add_metaclass(abc.ABCMeta) |
