From d08be320d5b6be0412b3b2a2bf297fa14606f8ae Mon Sep 17 00:00:00 2001 From: Adam McNevin Date: Wed, 3 Dec 2025 08:47:29 -0800 Subject: [PATCH 1/4] make md5 fips compliant --- castle/cms/_scripts/templates/watch-run.py | 2 +- castle/cms/archival.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/castle/cms/_scripts/templates/watch-run.py b/castle/cms/_scripts/templates/watch-run.py index 457c9f6f3..b97a96b92 100644 --- a/castle/cms/_scripts/templates/watch-run.py +++ b/castle/cms/_scripts/templates/watch-run.py @@ -13,7 +13,7 @@ def md5(fname): - hash_md5 = hashlib.md5() + hash_md5 = hashlib.new("md5", usedforsecurity=False) with open(fname, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) diff --git a/castle/cms/archival.py b/castle/cms/archival.py index 48217b722..e839a4071 100644 --- a/castle/cms/archival.py +++ b/castle/cms/archival.py @@ -440,7 +440,7 @@ def move_resource(self, url, keep_ext=False, use_vhm=True): fidata = fidata.replace(sub_url, new_url) # upload to amazon and get url! - md5 = hashlib.md5(fidata).hexdigest() + md5 = hashlib.new("md5", usedforsecurity=False).hexdigest() content_path = '{0}{1}/{2}/{3}/{4}'.format( RESOURCES_KEY_PREFIX, md5[0], md5[1], md5[2], md5 From 60a51a8f561bcf876845d323359c3cb02a5d0e84 Mon Sep 17 00:00:00 2001 From: Adam McNevin Date: Wed, 3 Dec 2025 08:48:55 -0800 Subject: [PATCH 2/4] update changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index d946ab127..4ee35854f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ Changelog - add script to parse worker logs for @@content-creator endpoint errors - add X-CASTLEMTA-PRIORITY header to 2fa emails +- update md5 for compliance 3.1.0b7 (2025-06-09) From c04138d559e0d29ad4a22f4c6d11f6aed2d134c3 Mon Sep 17 00:00:00 2001 From: Adam McNevin Date: Wed, 3 Dec 2025 10:58:11 -0800 Subject: [PATCH 3/4] move to utility --- castle/cms/_scripts/templates/watch-run.py | 4 ++-- castle/cms/archival.py | 4 ++-- castle/cms/utils/__init__.py | 1 + castle/cms/utils/misc.py | 13 +++++++++++++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/castle/cms/_scripts/templates/watch-run.py b/castle/cms/_scripts/templates/watch-run.py index b97a96b92..fa6d602e9 100644 --- a/castle/cms/_scripts/templates/watch-run.py +++ b/castle/cms/_scripts/templates/watch-run.py @@ -1,7 +1,7 @@ import os import argparse import time -import hashlib +from castle.cms.utils import md5_fips # noqa: E402 parser = argparse.ArgumentParser(description='') parser.add_argument('--command', dest='command') @@ -13,7 +13,7 @@ def md5(fname): - hash_md5 = hashlib.new("md5", usedforsecurity=False) + hash_md5 = md5_fips() with open(fname, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) diff --git a/castle/cms/archival.py b/castle/cms/archival.py index e839a4071..9a29269a9 100644 --- a/castle/cms/archival.py +++ b/castle/cms/archival.py @@ -7,7 +7,7 @@ from castle.cms import theming # noqa: E402 from castle.cms.files import aws # noqa: E402 from castle.cms.interfaces import IArchiveContentTransformer, IArchiveManager # noqa: E402 -from castle.cms.utils import normalize_url # noqa: E402 +from castle.cms.utils import normalize_url, md5_fips # noqa: E402 from DateTime import DateTime # noqa: E402 from lxml.html import fromstring # noqa: E402 from lxml.html import tostring # noqa: E402 @@ -440,7 +440,7 @@ def move_resource(self, url, keep_ext=False, use_vhm=True): fidata = fidata.replace(sub_url, new_url) # upload to amazon and get url! - md5 = hashlib.new("md5", usedforsecurity=False).hexdigest() + md5 = md5_fips().hexdigest() content_path = '{0}{1}/{2}/{3}/{4}'.format( RESOURCES_KEY_PREFIX, md5[0], md5[1], md5[2], md5 diff --git a/castle/cms/utils/__init__.py b/castle/cms/utils/__init__.py index eab7c601b..6896c5512 100644 --- a/castle/cms/utils/__init__.py +++ b/castle/cms/utils/__init__.py @@ -25,6 +25,7 @@ from .misc import get_random_string # noqa: F401 from .misc import json_dumps # noqa: F401 from .misc import make_random_key # noqa: F401 +from .misc import md5_fips # noqa: F401 from .misc import normalize_url # noqa: F401 from .misc import retriable # noqa: F401 from .misc import strings_differ # noqa: F401 diff --git a/castle/cms/utils/misc.py b/castle/cms/utils/misc.py index 5dc80bb7f..837bdc302 100644 --- a/castle/cms/utils/misc.py +++ b/castle/cms/utils/misc.py @@ -140,3 +140,16 @@ def _customhandler(obj): def json_dumps(data): return json.dumps(data, default=_customhandler) + + +def md5_fips(data=b''): + """FIPS-compatible MD5 constructor for non-security purposes.""" + try: + md5_hash = hashlib.new('md5', usedforsecurity=False) + except TypeError: + # in case FIPS is not supported + md5_hash = hashlib.md5() + + if data: + md5_hash.update(data) + return md5_hash From 251f678efc0a12251c820c48876e49d9301dfc45 Mon Sep 17 00:00:00 2001 From: Adam McNevin Date: Wed, 3 Dec 2025 11:38:15 -0800 Subject: [PATCH 4/4] changelog --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 4ee35854f..3075de8d8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,7 @@ Changelog - add script to parse worker logs for @@content-creator endpoint errors - add X-CASTLEMTA-PRIORITY header to 2fa emails -- update md5 for compliance +- update md5 usage for FIPS compliance 3.1.0b7 (2025-06-09)