[reportlab-users] ReportLab fails with ValueError on FIPS enabled systems
Andy Robinson
andy at reportlab.com
Mon Jun 30 02:38:35 EDT 2025
Martin, thanks for this! I had never heard of FIPS but this certainly
seems harmless and something we could include in an upcoming release. I'll
wait for my colleague Robin to review.
--
Andy Robinson
Managing Director, ReportLab
On Mon, 30 Jun 2025 at 02:14, Martin Renters via reportlab-users <
reportlab-users at lists2.reportlab.com> wrote:
> First of all, I’d like to thank the ReportLab people for releasing such a
> fabulous open-source toolkit. It truly makes generating PDFs so much easier!
>
> The problem I’m reporting affects users that have enabled FIPS mode on
> their systems. In my particular case, it is a RHEL 9.6 system. When in FIPS
> mode, certain cryptographic functions are not permitted because of
> weaknesses in the algorithms. In the case of ReportLab, this would be the
> MD5 function which is used to calculate a document ID and is also used to
> track fonts used, and a few other things, as well as in the PDF encryption
> functions themselves.
>
> When in FIPS mode, running a program will cause an exception similar to:
>
> Traceback (most recent call last):
>
> File "/opt/dftoolkit/bin/annotateCRF", line 82, in main
>
> annotate.build_pdf(plate_filter=args.plates)
>
> File
> "//opt/dftoolkit/lib/python3.9/site-packages/dftoolkit/annotate.py", line
> 549, in build_pdf
>
> self.doc.build(flowables)
>
> File
> "//opt/dftoolkit/lib/python3.9/site-packages/reportlab/platypus/doctemplate.py",
> line 1062, in build
>
> self._startBuild(filename,canvasmaker)
>
> File
> "//opt/dftoolkit/lib/python3.9/site-packages/reportlab/platypus/doctemplate.py",
> line 1032, in _startBuild
>
> self.canv = self._makeCanvas(filename=filename,canvasmaker=canvasmaker)
>
> File
> "//opt/dftoolkit/lib/python3.9/site-packages/reportlab/platypus/doctemplate.py",
> line 992, in _makeCanvas
>
> canv = canvasmaker(filename or self.filename,
>
> File
> "//opt/dftoolkit/lib/python3.9/site-packages/reportlab/pdfgen/canvas.py",
> line 320, in __init__
>
> self._doc = pdfdoc.PDFDocument(compression=pageCompression,
>
> File
> "//opt/dftoolkit/lib/python3.9/site-packages/reportlab/pdfbase/pdfdoc.py",
> line 137, in __init__
>
> sig = self.signature = md5()
>
> ValueError: [digital envelope routines] unsupported
>
> Most of the uses of MD5, aside from the use in the actual PDF encryption,
> appear not to be security related. In Python 3.9, the hashlib functions can
> be passed a keyword argument called ‘usedforsecurity’ (defaulting to True)
> that can be set to False if the hash algorithm isn’t used for security
> purposes. This allows the use of MD5 hashes even on FIPS enabled systems
> and allows ReportLab to successfully generate PDFs. Passing this keyword
> argument on the older Python 3.6.8 doesn’t cause any ill effects.
>
> A small test program demonstrating the hashlib problem on a FIPS enabled
> system, and the fix using usedforsecurity=False is as follows:
>
> $ more md5test.py
>
> from hashlib import md5
>
> m = md5()
>
> m.update(b'This is a test')
>
> print(m.hexdigest())
>
>
> $ python md5test.py
>
> Traceback (most recent call last):
>
> File "/home/martin/md5test.py", line 2, in <module>
>
> m = md5()
>
> ValueError: [digital envelope routines] unsupported
>
>
> Changing the m=md5() to m=md5(usedforsecurity=False) causes it to run
> successfully:
>
> $ more md5test.py
>
> from hashlib import md5
>
> m = md5(usedforsecurity=False)
>
> m.update(b'This is a test')
>
> print(m.hexdigest())
>
>
> $ python md5test.py
>
> ce114e4501d2f4e2dcea3e17b546f339
>
> Attached is a patch that adds the usedforsecurity=False parameter in the
> cases I believe are not security related. Alternatively, a different hash
> function permitted by FIPS could be used. The actual PDF encryption code
> isn’t modified by this patch and it will probably require the use of
> different algorithms to be FIPS compatible.
>
> Thanks for considering this patch,
>
> Martin
>
> diff -ru
> ../venv3.9/lib/python3.9/site-packages/reportlab/lib/fontfinder.py
> reportlab/lib/fontfinder.py
> --- ../venv3.9/lib/python3.9/site-packages/reportlab/lib/fontfinder.py 2025-06-27
> 12:37:23.507252855 -0400
> +++ reportlab/lib/fontfinder.py 2025-06-29 15:08:52.470092313 -0400
> @@ -216,7 +216,7 @@
> """Base this on the directories...same set of directories
> should give same cache"""
> fsEncoding = self._fsEncoding
> - hash = md5(b''.join(asBytes(_,enc=fsEncoding) for _ in
> sorted(self._dirs))).hexdigest()
> + hash = md5(b''.join(asBytes(_,enc=fsEncoding) for _ in
> sorted(self._dirs)),usedforsecurity=False).hexdigest()
> from reportlab.lib.utils import get_rl_tempfile
> fn = get_rl_tempfile('fonts_%s.dat' % hash)
> return fn
> diff -ru ../venv3.9/lib/python3.9/site-packages/reportlab/lib/utils.py
> reportlab/lib/utils.py
> --- ../venv3.9/lib/python3.9/site-packages/reportlab/lib/utils.py 2025-06-27
> 12:37:23.512252786 -0400
> +++ reportlab/lib/utils.py 2025-06-29 15:08:52.473092276 -0400
> @@ -55,7 +55,7 @@
> _rl_NoneType=type(None)
> strTypes = (str,bytes)
> def _digester(s):
> - return md5(s if isBytes(s) else s.encode('utf8')).hexdigest()
> + return md5(s if isBytes(s) else
> s.encode('utf8'),usedforsecurity=False).hexdigest()
>
> def asBytes(v,enc='utf8'):
> if isinstance(v,bytes): return v
> diff -ru
> ../venv3.9/lib/python3.9/site-packages/reportlab/pdfbase/cidfonts.py
> reportlab/pdfbase/cidfonts.py
> --- ../venv3.9/lib/python3.9/site-packages/reportlab/pdfbase/cidfonts.py 2025-06-27
> 12:37:23.519252690 -0400
> +++ reportlab/pdfbase/cidfonts.py 2025-06-29 15:08:52.668089932 -0400
> @@ -85,7 +85,7 @@
> self.parseCMAPFile(name)
>
> def _hash(self, text):
> - hasher = md5()
> + hasher = md5(usedforsecurity=False)
> hasher.update(text)
> return hasher.digest()
>
> diff -ru
> ../venv3.9/lib/python3.9/site-packages/reportlab/pdfbase/pdfdoc.py
> reportlab/pdfbase/pdfdoc.py
> --- ../venv3.9/lib/python3.9/site-packages/reportlab/pdfbase/pdfdoc.py 2025-06-27
> 12:37:23.520252676 -0400
> +++ reportlab/pdfbase/pdfdoc.py 2025-06-29 15:08:52.672089884 -0400
> @@ -134,7 +134,7 @@
> self.setCompression(compression)
> self._pdfVersion = pdfVersion
> # signature for creating PDF ID
> - sig = self.signature = md5()
> + sig = self.signature = md5(usedforsecurity=False)
> sig.update(b"a reportlab document")
> self._timeStamp = TimeStamp(self.invariant)
> cat = self._timeStamp.t
> diff -ru ../venv3.9/lib/python3.9/site-packages/reportlab/pdfgen/canvas.py
> reportlab/pdfgen/canvas.py
> --- ../venv3.9/lib/python3.9/site-packages/reportlab/pdfgen/canvas.py 2025-06-27
> 12:37:23.523252635 -0400
> +++ reportlab/pdfgen/canvas.py 2025-06-29 15:08:52.786088514 -0400
> @@ -1131,9 +1131,9 @@
> """
> #check if we've done this one already...
> if isUnicode(command):
> - rawName = 'PS' +
> hashlib.md5(command.encode('utf-8')).hexdigest()
> + rawName = 'PS' +
> hashlib.md5(command.encode('utf-8'),usedforsecurity=False).hexdigest()
> else:
> - rawName = 'PS' + hashlib.md5(command).hexdigest()
> + rawName = 'PS' +
> hashlib.md5(command,usedforsecurity=False).hexdigest()
> regName = self._doc.getXObjectName(rawName)
> psObj = self._doc.idToObject.get(regName, None)
> if not psObj:
>
> _______________________________________________
> reportlab-users mailing list
> reportlab-users at lists2.reportlab.com
> https://pairlist2.pair.net/mailman/listinfo/reportlab-users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://pairlist2.pair.net/pipermail/reportlab-users/attachments/20250630/a459d984/attachment-0001.htm>
More information about the reportlab-users
mailing list