[reportlab-users] ReportLab fails with ValueError on FIPS enabled systems

Martin Renters martin at teckelworks.com
Mon Jul 14 16:06:06 EDT 2025


Hi Andy,

I’m just following up to see how the review is going and whether you need anything else from me. Do you have an anticipated release date for the next version which will include this FIP fix?

Thanks,

Martin


> On Jun 30, 2025, at 2:38 AM, Andy Robinson <andy at reportlab.com> wrote:
> 
> 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 <mailto: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 <mailto: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/20250714/f5ea3f37/attachment-0001.htm>


More information about the reportlab-users mailing list