Introduction
Federal Information Processing Standard (FIPS) are a set of encryption algorithms and is mandatory in all computer systems and software used by non-military American government agencies, government contractors and vendors who work with the agencies. When new software is developed, it needs to be FIPS-compliant. Thus, there is a need to enable Python with FIPS, but the default Python package comes without FIPS as shown in screenshot below.
This blog will explain, step-by-step, how to enable Python 3 with the OpenSSL/FIPS standard on a Microsoft Windows platform so that any new software compiled out of it, is FIPS-compliant.
Prerequisites
- Cygwin environment
- Python 3
- Visual Studio 2017
STEPS
Step 1 Download the OpenSSL and FIPS source from http://www.openssl.org and the Python 3 source from http://www.python.org
Step 2 Convert all symlinks in the archive to regular files. This is done in Linux, Cygwin environment.
Untar
Zip -9$ tar -zxvf openssl-fips-2.0.16.tar.gz $ tar -zxvf openssl-1.0.2u.tar.gz $ tar -zxvf Python-3.8.6.tgz
Finally, unzip in Windows to the c:\work\ folder.$ zip -9 -r openssl-fips-2.0.16.zip openssl-fips-2.0.16 $ zip -9 -r openssl-1.0.2u.zip openssl-1.0.2u $ zip -9 -r Python-3.8.6.zip Python-3.8.6
Step 3 Install NASM for x64 and copy the contents of C:\Program Files\NASM to the C:\work\openssl-1.0.2u\ folder.
Note: NASM is a netwide assembler program and can be downloaded from [https://www.nasm.us/pub/nasm/releasebuilds]
Step 4 Build the FIPS module using the VS 2015 Native Tools Command prompt.
Rename the folder “out32dll” to “lib”.> cd openssl-fips-2.0.16 > ms\do_fips
Rename the folder “util” to “bin”.
Move “fips_standalone_sha1.exe” from “lib” to “bin”.
This is done so that OpenSSL can compile in the next steps.
Step 5 Build OpenSSL module using the VS 2015 Native Tools Command prompt.
cd openssl-1.0.2u perl Configure VC-WIN64A no-zlib no-idea no-mdc2 no-rc5 no-ssl2 no-ssl3 fips --with-fipslibdir=C:\usr\local\ssl\fips-2.0 ms\do_win64a nmake -f ms\nt.mak all nmake -f ms\nt.mak install
The openssl.exe and libeay32.dll and ssleay32.dll files are generated be in the C:\usr\local\ssl\bin\ folder.
Step 6 Unzip Python in Windows and create an 'externals' directory at the root (c:\work\Python-3.8.6) folder.
Under 'externals', create a directory for openssl-1.0.2u and copy all the contents of c:\usr\local\ssl\ to this directory.
Step 7 Under 'externals', create a directory for openssl-bin-1.0.2u/amd64 and copy all the files from c:\work\openssl-1.2.u\out32dll to this directory.
Also, copy all the files from c:\work\openssl-1.2.u\inc32 to the openssl-bin-1.0.2u/amd64/include directory.
Step 8 Patch/Add/Modify these codes to files as shown below under Python-3.8.6 source (c:\work\Python-3.8.6).
Lib\ssl.py:
Modules/_ssl.c:try: from _ssl import FIPS_mode, FIPS_mode_set print('successful import') except ImportError as e: print('error in importing') print(e)
Modules/clinic/_ssl.c.h:static PyObject * _ssl_FIPS_mode_impl(PyObject *module) { return PyLong_FromLong(FIPS_mode()); } static PyObject * _ssl_FIPS_mode_set_impl(PyObject *module, int n) { if (FIPS_mode_set(n) == 0) { _setSSLError(ERR_error_string(ERR_get_error(), NULL) , 0, __FILE__, __LINE__); return NULL; } Py_RETURN_NONE; } static PyMethodDef PySSL_methods[] = { _SSL__TEST_DECODE_CERT_METHODDEF _SSL_RAND_ADD_METHODDEF _SSL_RAND_BYTES_METHODDEF _SSL_RAND_PSEUDO_BYTES_METHODDEF _SSL_RAND_EGD_METHODDEF _SSL_RAND_STATUS_METHODDEF _SSL_GET_DEFAULT_VERIFY_PATHS_METHODDEF _SSL_ENUM_CERTIFICATES_METHODDEF _SSL_ENUM_CRLS_METHODDEF _SSL_TXT2OBJ_METHODDEF _SSL_NID2OBJ_METHODDEF _SSL_FIPS_MODE_METHODDEF _SSL_FIPS_MODE_SET_METHODDEF {NULL, NULL} /* Sentinel */ };
PyDoc_STRVAR(_ssl_FIPS_mode__doc__, "FIPS Mode"); #define _SSL_FIPS_MODE_METHODDEF \ {"FIPS_mode", (PyCFunction)_ssl_FIPS_mode, METH_NOARGS, _ssl_FIPS_mode__doc__}, static PyObject * _ssl_FIPS_mode_impl(PyObject *module); static PyObject * _ssl_FIPS_mode(PyObject *module, PyObject *Py_UNUSED(ignored)) { return _ssl_FIPS_mode_impl(module); } PyDoc_STRVAR(_ssl_FIPS_mode_set_doc__, "FIPS Mode Set"); #define _SSL_FIPS_MODE_SET_METHODDEF \ {"FIPS_mode_set", (PyCFunction)_ssl_FIPS_mode_set, METH_O, _ssl_FIPS_mode_set_doc__}, static PyObject * _ssl_FIPS_mode_set_impl(PyObject *module, int n); static PyObject * _ssl_FIPS_mode_set(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; int n; if (!PyArg_Parse(arg, "i:FIPS_mode_set", &n)) { goto exit; } return_value = _ssl_FIPS_mode_set_impl(module, n); exit: return return_value; }
The above code is used to enable functions ssl.FIPS_mode() and ssl.FIPS_mode_set().
Step 9 Modify PCbuild/openssl.props as shown below:
Step 10 Open PCbuild/python.props and change entries as shown below:
Step 11 Open Python Solution under PCbuild/pcbuild.sln in VS 2017/2015.
Change the link settings of _hashlib and _ssl projects under Python as shown below:
Change compile settings _hashlib and _ssl projects as shown below:
Step 12 Now, build _hashlib.pyd and _ssl.pyd in VS 2017/2015.
Step 13 Copy these built pyd files to a Python binary installation directory c:\python38\DLLs folder.
Copy ssl.py to c:\python38\Lib folder.
Copy OpenSSL DLLs (libeay32.dll and ssleay32.DLL) to the c:\python38\DLLs folder.
Step 14 Start Python and use these commands to check the OpenSSL/FIPS version.
Voila...now Python 3.8 has OpenSSL with FIPS.
Summary
In this blog, I have covered the following steps in regards to enabling Python 3 with FIPS.
- Download the required packages.
- Compile both OpenSSL and FIPS and link them both.
- Make the required changes to Python source before linking to the new OpenSSL and compile.
- Copy the newly generated binaries to the installed Python location.
- Test the version and check if FIPS is enabled.
I hope this blog is useful to the entire developer community!! Make sure you check out other blog posts on HPE DEV for more useful tutorials.