From 0fe30b0440270a1175cbf53007ed6fb534e16825 Mon Sep 17 00:00:00 2001
From: Bert Blommers <bblommers@users.noreply.github.com>
Date: Fri, 3 Jun 2022 09:41:30 +0000
Subject: [PATCH] MD5 - set usedforsecurity-parameter for all uses (#5190)

---
 moto/cognitoidp/models.py |  6 +++---
 moto/ec2/utils.py         |  4 ++--
 moto/efs/models.py        |  4 ++--
 moto/glacier/models.py    |  3 ++-
 moto/iam/models.py        |  4 ++--
 moto/kinesis/models.py    |  4 ++--
 moto/s3/models.py         |  7 +++----
 moto/sqs/models.py        |  5 +++--
 moto/utilities/utils.py   | 14 ++++++++++++++
 9 files changed, 33 insertions(+), 18 deletions(-)

diff --git a/moto/cognitoidp/models.py b/moto/cognitoidp/models.py
index 7c8a8e5cc..57a1f9c28 100644
--- a/moto/cognitoidp/models.py
+++ b/moto/cognitoidp/models.py
@@ -1,5 +1,4 @@
 import datetime
-import hashlib
 import json
 import os
 import time
@@ -31,6 +30,7 @@ from .utils import (
     PAGINATION_MODEL,
 )
 from moto.utilities.paginator import paginate
+from moto.utilities.utils import md5_hash
 
 
 class UserStatus(str, enum.Enum):
@@ -595,11 +595,11 @@ class CognitoIdpUserPoolDomain(BaseModel):
 
     def _distribution_name(self):
         if self.custom_domain_config and "CertificateArn" in self.custom_domain_config:
-            unique_hash = hashlib.md5(
+            unique_hash = md5_hash(
                 self.custom_domain_config["CertificateArn"].encode("utf-8")
             ).hexdigest()
             return f"{unique_hash[:16]}.cloudfront.net"
-        unique_hash = hashlib.md5(self.user_pool_id.encode("utf-8")).hexdigest()
+        unique_hash = md5_hash(self.user_pool_id.encode("utf-8")).hexdigest()
         return f"{unique_hash[:16]}.amazoncognito.com"
 
     def to_json(self, extended=True):
diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py
index 1d7a5f1e8..08ddcddf2 100644
--- a/moto/ec2/utils.py
+++ b/moto/ec2/utils.py
@@ -1,5 +1,4 @@
 import base64
-import hashlib
 import fnmatch
 import random
 import re
@@ -12,6 +11,7 @@ from cryptography.hazmat.primitives.asymmetric import rsa
 
 from moto.core import get_account_id
 from moto.iam import iam_backends
+from moto.utilities.utils import md5_hash
 
 EC2_RESOURCE_TO_PREFIX = {
     "customer-gateway": "cgw",
@@ -651,7 +651,7 @@ def rsa_public_key_fingerprint(rsa_public_key):
         encoding=serialization.Encoding.DER,
         format=serialization.PublicFormat.SubjectPublicKeyInfo,
     )
-    fingerprint_hex = hashlib.md5(key_data).hexdigest()
+    fingerprint_hex = md5_hash(key_data).hexdigest()
     fingerprint = re.sub(r"([a-f0-9]{2})(?!$)", r"\1:", fingerprint_hex)
     return fingerprint
 
diff --git a/moto/efs/models.py b/moto/efs/models.py
index 29a4ebf01..375968562 100644
--- a/moto/efs/models.py
+++ b/moto/efs/models.py
@@ -7,7 +7,6 @@ https://docs.aws.amazon.com/efs/latest/ug/whatisefs.html
 import json
 import time
 from copy import deepcopy
-from hashlib import md5
 
 from moto.core import get_account_id, BaseBackend, BaseModel, CloudFormationModel
 from moto.core.utils import (
@@ -32,6 +31,7 @@ from moto.efs.exceptions import (
     SecurityGroupLimitExceeded,
 )
 from moto.utilities.tagging_service import TaggingService
+from moto.utilities.utils import md5_hash
 
 
 def _lookup_az_id(az_name):
@@ -382,7 +382,7 @@ class EFSBackend(BaseBackend):
         if max_items < len(corpus):
             new_corpus = corpus[max_items:]
             new_corpus_dict = [c.info_json() for c in new_corpus]
-            new_hash = md5(json.dumps(new_corpus_dict).encode("utf-8"))
+            new_hash = md5_hash(json.dumps(new_corpus_dict).encode("utf-8"))
             next_marker = new_hash.hexdigest()
             self.next_markers[next_marker] = new_corpus
         else:
diff --git a/moto/glacier/models.py b/moto/glacier/models.py
index a9fb35286..7066c219a 100644
--- a/moto/glacier/models.py
+++ b/moto/glacier/models.py
@@ -4,6 +4,7 @@ import datetime
 
 from moto.core import get_account_id, BaseBackend, BaseModel
 from moto.core.utils import BackendDict
+from moto.utilities.utils import md5_hash
 
 from .utils import get_job_id
 
@@ -117,7 +118,7 @@ class Vault(BaseModel):
         return d
 
     def create_archive(self, body, description):
-        archive_id = hashlib.md5(body).hexdigest()
+        archive_id = md5_hash(body).hexdigest()
         self.archives[archive_id] = {}
         self.archives[archive_id]["archive_id"] = archive_id
         self.archives[archive_id]["body"] = body
diff --git a/moto/iam/models.py b/moto/iam/models.py
index cecf2cc57..0f857be71 100644
--- a/moto/iam/models.py
+++ b/moto/iam/models.py
@@ -1,5 +1,4 @@
 import base64
-import hashlib
 import os
 import random
 import string
@@ -22,6 +21,7 @@ from moto.core.utils import (
     iso_8601_datetime_with_milliseconds,
 )
 from moto.iam.policy_validation import IAMPolicyDocumentValidator
+from moto.utilities.utils import md5_hash
 
 from .aws_managed_policies import aws_managed_policies_data
 from .exceptions import (
@@ -1014,7 +1014,7 @@ class SshPublicKey(BaseModel):
         self.user_name = user_name
         self.ssh_public_key_body = ssh_public_key_body
         self.ssh_public_key_id = "APKA" + random_access_key()
-        self.fingerprint = hashlib.md5(ssh_public_key_body.encode()).hexdigest()
+        self.fingerprint = md5_hash(ssh_public_key_body.encode()).hexdigest()
         self.status = "Active"
         self.upload_date = datetime.utcnow()
 
diff --git a/moto/kinesis/models.py b/moto/kinesis/models.py
index f772d304c..4e6c21958 100644
--- a/moto/kinesis/models.py
+++ b/moto/kinesis/models.py
@@ -4,12 +4,12 @@ import re
 import itertools
 
 from operator import attrgetter
-from hashlib import md5
 
 from moto.core import BaseBackend, BaseModel, CloudFormationModel
 from moto.core.utils import unix_time, BackendDict
 from moto.core import get_account_id
 from moto.utilities.paginator import paginate
+from moto.utilities.utils import md5_hash
 from .exceptions import (
     ConsumerNotFound,
     StreamNotFoundError,
@@ -363,7 +363,7 @@ class Stream(CloudFormationModel):
                 raise InvalidArgumentError("explicit_hash_key")
 
         else:
-            key = int(md5(partition_key.encode("utf-8")).hexdigest(), 16)
+            key = int(md5_hash(partition_key.encode("utf-8")).hexdigest(), 16)
 
         for shard in self.shards.values():
             if shard.starting_hash <= key < shard.ending_hash:
diff --git a/moto/s3/models.py b/moto/s3/models.py
index 82fb4dbb4..75596bb6a 100644
--- a/moto/s3/models.py
+++ b/moto/s3/models.py
@@ -2,7 +2,6 @@ import json
 import os
 import base64
 import datetime
-import hashlib
 import copy
 import itertools
 import codecs
@@ -33,7 +32,7 @@ from moto.core.utils import (
 )
 from moto.cloudwatch.models import MetricDatum
 from moto.utilities.tagging_service import TaggingService
-from moto.utilities.utils import LowercaseDict
+from moto.utilities.utils import LowercaseDict, md5_hash
 from moto.s3.exceptions import (
     AccessDeniedByLock,
     BucketAlreadyExists,
@@ -213,7 +212,7 @@ class FakeKey(BaseModel):
     @property
     def etag(self):
         if self._etag is None:
-            value_md5 = hashlib.md5()
+            value_md5 = md5_hash()
             self._value_buffer.seek(0)
             while True:
                 block = self._value_buffer.read(16 * 1024 * 1024)  # read in 16MB chunks
@@ -376,7 +375,7 @@ class FakeMultipart(BaseModel):
         if count == 0:
             raise MalformedXML
 
-        etag = hashlib.md5()
+        etag = md5_hash()
         etag.update(bytes(md5s))
         return total, "{0}-{1}".format(etag.hexdigest(), count)
 
diff --git a/moto/sqs/models.py b/moto/sqs/models.py
index efc0c5f07..d0528d79a 100644
--- a/moto/sqs/models.py
+++ b/moto/sqs/models.py
@@ -20,6 +20,7 @@ from moto.core.utils import (
     tags_from_cloudformation_tags_list,
     BackendDict,
 )
+from moto.utilities.utils import md5_hash
 from .utils import generate_receipt_handle
 from .exceptions import (
     MessageAttributesInvalid,
@@ -85,14 +86,14 @@ class Message(BaseModel):
 
     @property
     def body_md5(self):
-        md5 = hashlib.md5()
+        md5 = md5_hash()
         md5.update(self._body.encode("utf-8"))
         return md5.hexdigest()
 
     @property
     def attribute_md5(self):
 
-        md5 = hashlib.md5()
+        md5 = md5_hash()
 
         for attrName in sorted(self.message_attributes.keys()):
             self.validate_attribute_name(attrName)
diff --git a/moto/utilities/utils.py b/moto/utilities/utils.py
index 006235956..636e71e1e 100644
--- a/moto/utilities/utils.py
+++ b/moto/utilities/utils.py
@@ -1,4 +1,5 @@
 import json
+import hashlib
 import random
 import string
 import pkgutil
@@ -57,6 +58,19 @@ def filter_resources(resources, filters, attr_pairs):
     return result
 
 
+def md5_hash(data=None):
+    """
+    MD5-hashing for non-security usecases.
+    Required for Moto to work in FIPS-enabled systems
+    """
+    args = (data,) if data else ()
+    try:
+        return hashlib.md5(*args, usedforsecurity=False)
+    except TypeError:
+        # The usedforsecurity-parameter is only available as of Python 3.9
+        return hashlib.md5(*args)
+
+
 class LowercaseDict(MutableMapping):
     """A dictionary that lowercases all keys"""