Browse Source

Replace md5 with oslo version

md5 is not an approved algorithm in FIPS mode, and trying to
instantiate a hashlib.md5() will fail when the system is running in
FIPS mode.

md5 is allowed when in a non-security context.  There is a plan to
add a keyword parameter (usedforsecurity) to hashlib.md5() to annotate
whether or not the instance is being used in a security context.

In the case where it is not, the instantiation of md5 will be allowed.
See https://bugs.python.org/issue9216 for more details.

Some downstream python versions already support this parameter.  To
support these versions, a new encapsulation of md5() has been added to
oslo_utils.  See https://review.opendev.org/#/c/750031/

This patch is to replace the instances of hashlib.md5() with this new
encapsulation, adding an annotation indicating whether the usage is
a security context or not.

Reviewers need to pay particular attention as to whether the keyword
parameter (usedforsecurity) is set correctly.

Change-Id: Idbef0f0896753765372c8dfac8ab15e6be49922f
Depends-On: https://review.opendev.org/#/c/760160
changes/32/756432/2 2.8.0
Ade Lee 6 months ago
parent
commit
54448e9d8b
5 changed files with 32 additions and 11 deletions
  1. +1
    -1
      requirements.txt
  2. +3
    -2
      tooz/drivers/ipc.py
  3. +2
    -2
      tooz/drivers/pgsql.py
  4. +15
    -3
      tooz/hashring.py
  5. +11
    -3
      tooz/tests/test_hashring.py

+ 1
- 1
requirements.txt View File

@ -8,5 +8,5 @@ msgpack>=0.4.0 # Apache-2.0
fasteners>=0.7 # Apache-2.0
tenacity>=3.2.1 # Apache-2.0
futurist>=1.2.0 # Apache-2.0
oslo.utils>=3.15.0 # Apache-2.0
oslo.utils>=4.7.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0

+ 3
- 2
tooz/drivers/ipc.py View File

@ -14,13 +14,14 @@
# License for the specific language governing permissions and limitations
# under the License.
import hashlib
import struct
import time
import msgpack
import sysv_ipc
from oslo_utils.secretutils import md5
import tooz
from tooz import coordination
from tooz import locking
@ -35,7 +36,7 @@ else:
def ftok(name, project):
# Similar to ftok & http://semanchuk.com/philip/sysv_ipc/#ftok_weakness
# but hopefully without as many weaknesses...
h = hashlib.md5()
h = md5(usedforsecurity=False)
if not isinstance(project, bytes):
project = project.encode('ascii')
h.update(project)


+ 2
- 2
tooz/drivers/pgsql.py View File

@ -15,10 +15,10 @@
# under the License.
import contextlib
import hashlib
import logging
from oslo_utils import encodeutils
from oslo_utils.secretutils import md5
import psycopg2
import tooz
@ -98,7 +98,7 @@ class PostgresLock(locking.Lock):
self._conn = None
self._parsed_url = parsed_url
self._options = options
h = hashlib.md5()
h = md5(usedforsecurity=False)
h.update(name)
self.key = h.digest()[0:2]


+ 15
- 3
tooz/hashring.py View File

@ -16,6 +16,8 @@
import bisect
import hashlib
from oslo_utils.secretutils import md5
import tooz
from tooz import utils
@ -80,7 +82,10 @@ class HashRing(object):
"""
for node in nodes:
key = utils.to_binary(node, 'utf-8')
key_hash = hashlib.new(self._hash_function, key)
if self._hash_function == 'md5':
key_hash = md5(key, usedforsecurity=False)
else:
key_hash = hashlib.new(self._hash_function, key)
for r in range(self._partition_number * weight):
key_hash.update(key)
self._ring[self._hash2int(key_hash)] = node
@ -102,7 +107,10 @@ class HashRing(object):
raise UnknownNode(node)
key = utils.to_binary(node, 'utf-8')
key_hash = hashlib.new(self._hash_function, key)
if self._hash_function == 'md5':
key_hash = md5(key, usedforsecurity=False)
else:
key_hash = hashlib.new(self._hash_function, key)
for r in range(self._partition_number * weight):
key_hash.update(key)
del self._ring[self._hash2int(key_hash)]
@ -114,7 +122,11 @@ class HashRing(object):
return int(key.hexdigest(), 16)
def _get_partition(self, data):
hashed_key = self._hash2int(hashlib.new(self._hash_function, data))
if self._hash_function == 'md5':
hashed_key = self._hash2int(md5(data, usedforsecurity=False))
else:
hashed_key = self._hash2int(
hashlib.new(self._hash_function, data))
position = bisect.bisect(self._partitions, hashed_key)
return position if position < len(self._partitions) else 0


+ 11
- 3
tooz/tests/test_hashring.py View File

@ -31,7 +31,13 @@ class HashRingTestCase(testcase.TestCase):
# fake -> foo, bar, baz
# fake-again -> bar, baz, foo
@mock.patch.object(hashlib, 'new', autospec=True)
try:
_ = hashlib.md5(usedforsecurity=False)
_fips_enabled = True
except TypeError:
_fips_enabled = False
@mock.patch.object(hashlib, 'md5', autospec=True)
def test_hash2int_returns_int(self, mock_md5):
r1 = 32 * 'a'
r2 = 32 * 'b'
@ -44,8 +50,10 @@ class HashRingTestCase(testcase.TestCase):
self.assertIn(int(r1, 16), ring._ring)
self.assertIn(int(r2, 16), ring._ring)
mock_md5.assert_called_with('md5', mock.ANY)
if self._fips_enabled:
mock_md5.assert_called_with(mock.ANY, usedforsecurity=False)
else:
mock_md5.assert_called_with(mock.ANY)
def test_create_ring(self):
nodes = {'foo', 'bar'}


Loading…
Cancel
Save