From f80e28767f60f90b6f0caa0c0dca17accf99ce71 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Thu, 2 Oct 2014 16:38:44 -0500 Subject: enable truncation for DSA digests prior to signing This works around a bug in OpenSSL 1.0.0, 1.0.0a, and 1.0.0b where SHA-2 is a supported signature algorithm but FIPS 186-3 truncation is not performed if the q value is smaller than the digest output length --- cryptography/hazmat/backends/openssl/dsa.py | 20 ++++++++++++++++++++ cryptography/hazmat/backends/openssl/ec.py | 20 ++------------------ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/dsa.py b/cryptography/hazmat/backends/openssl/dsa.py index 5e7a26ff..4b5ca59c 100644 --- a/cryptography/hazmat/backends/openssl/dsa.py +++ b/cryptography/hazmat/backends/openssl/dsa.py @@ -15,6 +15,7 @@ from __future__ import absolute_import, division, print_function from cryptography import utils from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.backends.openssl.utils import _truncate_digest from cryptography.hazmat.primitives import hashes, interfaces from cryptography.hazmat.primitives.asymmetric import dsa from cryptography.hazmat.primitives.interfaces import ( @@ -22,6 +23,18 @@ from cryptography.hazmat.primitives.interfaces import ( ) +def _truncate_digest_for_dsa(dsa_cdata, digest, backend): + """ + This function truncates digests that are longer than a given DS + key's length so they can be signed. OpenSSL does this for us in + 1.0.0c+ and it isn't needed in 0.9.8, but that leaves us with three + releases (1.0.0, 1.0.0a, and 1.0.0b) where this is a problem. + """ + + order_bits = backend._lib.BN_num_bits(dsa_cdata.q) + return _truncate_digest(digest, order_bits) + + @utils.register_interface(interfaces.AsymmetricVerificationContext) class _DSAVerificationContext(object): def __init__(self, backend, public_key, signature, algorithm): @@ -41,6 +54,10 @@ class _DSAVerificationContext(object): data_to_verify = self._hash_ctx.finalize() + data_to_verify = _truncate_digest_for_dsa( + self._dsa_cdata, data_to_verify, self._backend + ) + # The first parameter passed to DSA_verify is unused by OpenSSL but # must be an integer. res = self._backend._lib.DSA_verify( @@ -69,6 +86,9 @@ class _DSASignatureContext(object): def finalize(self): data_to_sign = self._hash_ctx.finalize() + data_to_sign = _truncate_digest_for_dsa( + self._private_key._dsa_cdata, data_to_sign, self._backend + ) 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 *") diff --git a/cryptography/hazmat/backends/openssl/ec.py b/cryptography/hazmat/backends/openssl/ec.py index 9371a9a9..7798c3dc 100644 --- a/cryptography/hazmat/backends/openssl/ec.py +++ b/cryptography/hazmat/backends/openssl/ec.py @@ -13,12 +13,11 @@ from __future__ import absolute_import, division, print_function -import six - from cryptography import utils from cryptography.exceptions import ( InvalidSignature, UnsupportedAlgorithm, _Reasons ) +from cryptography.hazmat.backends.openssl.utils import _truncate_digest from cryptography.hazmat.primitives import hashes, interfaces from cryptography.hazmat.primitives.asymmetric import ec @@ -34,8 +33,6 @@ 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) with backend._tmp_bn_ctx() as bn_ctx: @@ -47,20 +44,7 @@ def _truncate_digest_for_ecdsa(ec_key_cdata, digest, backend): 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 + return _truncate_digest(digest, order_bits) def _ec_key_curve_sn(backend, ec_key): -- cgit v1.2.3 From b181f2a57d57caaa6e53e193e88002a15e284fd0 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 3 Oct 2014 14:50:48 -0500 Subject: add the missing file. i are senior dvlpr --- cryptography/hazmat/backends/openssl/utils.py | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 cryptography/hazmat/backends/openssl/utils.py diff --git a/cryptography/hazmat/backends/openssl/utils.py b/cryptography/hazmat/backends/openssl/utils.py new file mode 100644 index 00000000..408b6146 --- /dev/null +++ b/cryptography/hazmat/backends/openssl/utils.py @@ -0,0 +1,35 @@ +# 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 + + +def _truncate_digest(digest, order_bits): + digest_len = len(digest) + + 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 -- cgit v1.2.3 From bb20dd5fb7d55f4ccea3dd5a3a32536722d3456b Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 3 Oct 2014 16:04:13 -0500 Subject: update comment with info re: 0.9.8 --- cryptography/hazmat/backends/openssl/dsa.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cryptography/hazmat/backends/openssl/dsa.py b/cryptography/hazmat/backends/openssl/dsa.py index 4b5ca59c..3fb67a5d 100644 --- a/cryptography/hazmat/backends/openssl/dsa.py +++ b/cryptography/hazmat/backends/openssl/dsa.py @@ -28,7 +28,8 @@ def _truncate_digest_for_dsa(dsa_cdata, digest, backend): This function truncates digests that are longer than a given DS key's length so they can be signed. OpenSSL does this for us in 1.0.0c+ and it isn't needed in 0.9.8, but that leaves us with three - releases (1.0.0, 1.0.0a, and 1.0.0b) where this is a problem. + releases (1.0.0, 1.0.0a, and 1.0.0b) where this is a problem. This + truncation is not required in 0.9.8 because DSA is limited to SHA-1. """ order_bits = backend._lib.BN_num_bits(dsa_cdata.q) -- cgit v1.2.3