BUG: Building numpy on a FIPS mode system fails due to use of md5

Describe the issue:

Linux systems with FIPS mode active disable algorithms that aren't part of the FIPS standard such as the md5 message digest/checksum.

Reproduce the code example:

If I install numpy from the git version:

$ spin build

numpy/_core/meson.build:55:4: ERROR: Command `/usr/bin/python3 /home/user/current/lido/numpy/numpy/_core/code_generators/verify_c_api_version.py --api-version 0x00000013` failed with status 1.

A full log can be found at /home/user/current/lido/numpy/build/meson-logs/meson-log.txt

Traceback (most recent call last):
  File "/usr/lib/python3.12/site-packages/click/core.py", line 781, in forward
    return __self.invoke(__cmd, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/.local/lib/python3.12/site-packages/spin/cmds/meson.py", line 289, in build
    raise RuntimeError(
RuntimeError: Meson configuration failed; please try `spin build` again with the `--clean` flag.

An internal error has occurred. Please file a bug report at

  https://github.com/scientific-python/spin

including the above traceback and the following information:

  spin: 0.11, package: numpy

Aborting.

Error message:

Looking in `meson-log.txt`, here is if the error message:

Program _build_utils/tempita.py found: YES (/usr/bin/python3 /home/user/current/lido/numpy/numpy/_build_utils/tempita.py)
Running command: /usr/bin/python3 --version
--- stdout ---
Python 3.12.4

--- stderr ---


Configuring __config__.py using configuration
Running command: /usr/bin/python3 /home/user/current/lido/numpy/numpy/_core/code_generators/verify_c_api_version.py --api-version 0x00000013
--- stdout ---

--- stderr ---
Traceback (most recent call last):
  File "/home/user/current/lido/numpy/numpy/_core/code_generators/verify_c_api_version.py", line 66, in <module>
    main()
  File "/home/user/current/lido/numpy/numpy/_core/code_generators/verify_c_api_version.py", line 62, in main
    check_api_version(int(args.api_version, base=16))
  File "/home/user/current/lido/numpy/numpy/_core/code_generators/verify_c_api_version.py", line 35, in check_api_version
    curapi_hash, api_hash = get_api_versions(apiversion)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/current/lido/numpy/numpy/_core/code_generators/verify_c_api_version.py", line 25, in get_api_versions
    curapi_hash = m.fullapi_hash(numpy_api.full_api)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/current/lido/numpy/numpy/_core/code_generators/genapi.py", line 536, in fullapi_hash
    return hashlib.md5(''.join(a).encode('ascii')).hexdigest()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_hashlib.UnsupportedDigestmodError: [digital envelope routines] unsupported



numpy/_core/meson.build:55:4: ERROR: Command `/usr/bin/python3 /home/user/current/lido/numpy/numpy/_core/code_generators/verify_c_api_version.py --api-version 0x00000013` failed with status 1.


### Python and NumPy Versions:

[user@machine numpy]$ python --version
Python 3.12.4
I used git main branch version.


### Runtime Environment:

The key aspect on my runtime environment is that Linux FIPS mode is activated.
It's a Fedora 39 system.

### Context for the issue:

This patch fixes the issue:

$ cat py-numpy/num-py-fips-mode.patch
--- a/numpy/core/code_generators/genapi.py~ 2024-02-05 13:17:48.000000000 -0800
+++ b/numpy/core/code_generators/genapi.py 2024-08-02 06:42:20.346265686 -0700
@@ -153,7 +153,13 @@
return '%s%s %s(%s)' % (doccomment, self.return_type, self.name, argstr)

  •        if hashlib._hashlib.get_fips_mode():
    
  •            m = hashlib.md5(usedforsecurity=False)
    
  •        m = hashlib.md5()
       m.update(remove_whitespace(self.return_type))
       m.update('\000')
       m.update(self.name)
    

@@ -517,7 +523,14 @@
a.extend(name)
a.extend(','.join(map(str, data)))

  • return hashlib.md5(''.join(a).encode('ascii')).hexdigest()
  • try:
  •    if hashlib._hashlib.get_fips_mode():
    
  •        return hashlib.md5(''.join(a).encode('ascii'),usedforsecurity=False).hexdigest()
    
  •        return hashlib.md5(''.join(a).encode('ascii'),usedforsecurity=False).hexdigest()
    
  • except:
  •    return hashlib.md5(''.join(a).encode('ascii')).hexdigest()
    

To parse strings like 'hex = checksum' where hex is e.g. 0x1234567F and

checksum a 128 bits md5 checksum (hex format as well)

@@ -539,7 +552,14 @@
tagname = sys.argv[1]
order_file = sys.argv[2]
functions = get_api_functions(tagname, order_file)

  • m = hashlib.md5(tagname)
  • try:
  •    if hashlib._hashlib.get_fips_mode():
    
  •        m = hashlib.md5(tagname, usedforsecurity=False)
    
  • except:
  • for func in functions:
    print(func)
    ah = func.api_hash()