OpenSSL FIPS_mode_set 在 Python 加密库中不工作
OpenSSL FIPS_mode_set not working in Python cryptography library
根据 Python 密码学库的文档 [1],可以使用静态链接的 OpenSSL 构建自定义密码学轮。我尝试使用使用 FIPS 对象模块构建的 OpenSSL 安装来执行此操作,并且能够成功构建轮子,但发现它没有 FIPS 功能(无法设置 FIPS_mode_set=1)。
我创建了一个可以重现相同结果的 Dockerfile。最后的 Python 代码应该显示“1”和 "OpenSSL 1.0.2t-fips 10 Sep 2019" 而不是显示“0”和 "OpenSSL 1.0.2t 10 Sep 2019"(没有 -fips
指定)。
让我感到困惑的是,当我调用我构建的 openssl version
CLI 时,它正确地显示了带有 -fips
后缀的版本。因为,我猜我在构建密码学的某个地方出错了。
在此感谢任何帮助!
FROM centos
# Install build dependencies
RUN yum groupinstall -y "Development Tools" && \
yum install -y python-devel libffi-devel
# Install Python dependencies
RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
python get-pip.py && \
pip install virtualenv setuptools wheel pip
# Build Fips object module
RUN curl -O https://www.openssl.org/source/openssl-fips-2.0.16.tar.gz && \
tar xvf openssl-fips-2.0.16.tar.gz && \
cd openssl-fips-2.0.16 && \
./config && \
make && \
make install
# Build OpenSSL
RUN curl -O https://www.openssl.org/source/openssl-1.0.2t.tar.gz && \
tar xvf openssl-1.0.2t.tar.gz && \
cd /openssl-1.0.2t && \
./config fips no-shared -fPIC --prefix=/openssl-1.0.2t/openssl && \
make depend && \
make && \
make install_sw
# Build cryptography
RUN CFLAGS="-I/openssl-1.0.2t/openssl/include" LDFLAGS="-L/openssl-1.0.2t/openssl/lib" pip wheel --no-cache --no-binary :all: cryptography && \
pip install cryptography*.whl
# Test if fips is enabled
RUN python -c "\
from cryptography.hazmat.backends.openssl.backend import backend;\
print backend._lib.FIPS_mode_set(1);\
print ''.join([backend._lib.OPENSSL_VERSION_TEXT[i] for i in range(30)])"
[1] https://cryptography.io/en/latest/installation/#static-wheels
编辑: 通过将 -DOPENSSL_FIPS
添加到加密构建中,我能够使 OPENSSL_VERSION_TEXT
的输出变为 OpenSSL 1.0.2t-fips 10 Sep 20
但输出FIPS_mode_set(1) 仍然是 0。
编辑 2: 使用 ERR_get_error()
显示以下内容:
>>> print backend._lib.FIPS_mode_set(1)
0
>>> print backend._lib.ERR_get_error()
755413103
当我将其放入 openssl errstr
时,我得到:
openssl errstr 755413103
error:755413103:lib(85):func(1043):reason(259)
根据一些 Google 搜索,这表明指纹不匹配 (FIPS_R_FINGERPRINT_DOES_NOT_MATCH
)。虽然不确定从这里去哪里。
1。一般
首先,我想提一下,虽然我理解原因,但我并不完全同意 Cryptography 的观点 ([Cryptography]: Installation - Static Wheels)。共享库存在了几十年,并证明了它们的优越性。更不用说 Python 运送 2(标准)模块(_ssl 和 _hashlib) 动态 link 到 OpenSSL (无论它在系统上是什么)。作为旁注,在 Win 上,2 个 Python 模块也用于 link 静态 OpenSSL,但从 v3.7 开始,他们不再这样做了。回到 Nix:2 个 OpenSSL 版本被加载到同一个 (Python) 进程中.好像没什么害处,反而看起来好笑。正如今天的情况 (191009),v2.7[=296= 有一堆 .whls ] 和 v3.4,但是 none 对于相当不错的 (Python) 环境:
我记得前段时间类似的情况:除了2个标准模块外,还使用了M2Crypto。在那种情况下(我们完全运送 Python),2 个特定的(FIPS 支持)OpenSSL (dynamic) libs 也已发货,所有 Python 模块link 告诉他们。它适用于各种(桌面)环境(其中有许多“异国情调”的环境):
- CPU 架构(LE / BE):x86, AMD64, IA64, SPARC, PPC, zSeries
- OSes(有多个版本):Win、Lnx (RH,CentOS,OEL,SuSE, Xen, Ubuntu), Solaris, AIX, HP-UX(作为个人练习,我添加了OSX)
[OpenSSL]: UserGuide-2.0.pdf - User Guide for the OpenSSL FIPS Object Module v2.0 (referenced from [OpenSSL]: FIPS-140(如果 URL 发生变化)包含所有需要的详细信息。
在继续之前,这里有一些我将在 post 中使用的术语:
- FOM - FIPS 对象模块 (fipscanister.o)
- FOME - 可执行文件 (ELF (PE on Win )) FOM 是 linked in。请记住,它可以是可执行文件 本身 ,也可以是 .so (.dll).此外,如果它包含在静态库 (.a) 中,则不会 linked(只是存档)。作为旁注,当 OpenSSL 构建共享时,FOME 是 libcrypto.so.* ,而当静态构建时(如本例),它是 link 与 libcrypto.a 的可执行文件(例如 openssl可执行)
FOM 位于 OpenSSL 之上(可能还有其他类似的加密提供程序,例如 LibreSSL, WolfSSL, ),它旨在通过限制一些原本可用的功能来加强安全性(根据 NIST 标准)。一个这样的功能示例是 md5 哈希的使用,它被认为是弱的(我很确定 sha1 也将在即将发布的下一个版本中跟进。
这是(在 运行 时间)发生的事情的(非常简化的)版本:
- FIPS 模式开启:
- 检查是否满足附加约束:
- 是:继续(使用默认功能)
- 否:Return 有错误
- FIPS 模式关闭:
- 回退到默认功能
#1.1.部分为自测。其中包括:
- 计算 FOME 签名
- 将它与一个值(也存储在 FOME 中)进行比较
这恰好确保(或大大减少机会)没有人篡改(手动修改、反汇编,...)FOME。为了更好地理解签名机制,让我们深入了解 FOME 构建过程:
- 所有 FOME 来源 + FOM 都被编译(成目标文件)
- 它们被 link 编在一起(进入 FOME)。这是正常构建结束的时候
- 正在计算FOME签名
- 来自 #1. + fips_premain.o 的项目正在 link 编辑成(真实的,非.dll) 可执行文件 (FPD)
针对 FOME (#1.) 调用 - FPD。它读取 FOME 的 .rodata 部分并计算其 sha1 哈希。请注意,它会忽略位于特定地址41 字节区域(打孔)
重复 - #3.1.,但这次 fips_premain.o 被重新编译以包含上一步的哈希值。现在可以清楚地看到上一步的打孔,它是签名所在的位置:(length of sha hash (40) + nul)。 这是最终的FOME
注意:在 Win 上,情况略有不同。
我已经成功地重现了这个问题。我将从测试脚本开始。
code00.py:
#!/usr/bin/env python3
import sys
from cryptography.hazmat.backends.openssl.backend import backend
import cffi
def main():
ffi = cffi.FFI()
lib = backend._lib
fmt = "OpenSSL version: {0:s}\nFIPS_mode(): {1:d}\nFIPS_mode_set(1): {2:d}\nFIPS_mode(): {3:d}"
print(fmt.format(ffi.string(lib.OPENSSL_VERSION_TEXT).decode(),
lib.FIPS_mode(), lib.FIPS_mode_set(1), lib.FIPS_mode()))
err = lib.ERR_get_error()
if err:
err_fmt = "error:[{0:d}]:[{1:s}]:[{2:s}]:[{3:s}]"
print(err_fmt.format(
err,
ffi.string(lib.ERR_lib_error_string(err)).decode(),
ffi.string(lib.ERR_func_error_string(err)).decode(),
ffi.string(lib.ERR_reason_error_string(err)).decode()
))
else:
print("Success !!!")
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main()
print("\nDone.")
2。设置
我已经构建了 FOM 和 FIPS 能力 OpenSSL(类似于你的,但我定制了他们的路径)。在构建 FOM 和 OpenSSL.
时使用了 ${FIPSDIR} 变量
[cfati@cfati-ubtu16x64-0:~/Work/Dev/Whosebug/q058228435]> ~/sopr.sh
*** Set shorter prompt to better fit when pasted in Whosebug (or other) pages ***
[064bit-prompt]> uname -a
Linux cfati-ubtu16x64-0 4.15.0-65-generic #74~16.04.1-Ubuntu SMP Wed Sep 18 09:51:44 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
[064bit-prompt]> cat /etc/lsb-release | grep DESCR
DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"
[064bit-prompt]> gcc --version | grep gcc
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
[064bit-prompt]>
[064bit-prompt]> echo ${FIPSDIR}
/home/cfati/Work/Dev/Tools/zzz_Build/OpenSSL/int/openssl-fips-2.0.16
[064bit-prompt]> tree ${FIPSDIR}
/home/cfati/Work/Dev/Tools/zzz_Build/OpenSSL/int/openssl-fips-2.0.16
├── bin
│ ├── fipsld
│ └── fips_standalone_sha1
├── include
│ └── openssl
│ ├── aes.h
│ ├── bn.h
│ ├── buffer.h
│ ├── cmac.h
│ ├── crypto.h
│ ├── des.h
│ ├── des_old.h
│ ├── dh.h
│ ├── dsa.h
│ ├── ebcdic.h
│ ├── ecdh.h
│ ├── ecdsa.h
│ ├── ec.h
│ ├── e_os2.h
│ ├── evp.h
│ ├── fips.h
│ ├── fips_rand.h
│ ├── fipssyms.h
│ ├── hmac.h
│ ├── modes.h
│ ├── opensslconf.h
│ ├── opensslv.h
│ ├── ossl_typ.h
│ ├── rsa.h
│ ├── sha.h
│ └── symhacks.h
└── lib
├── fipscanister.o
├── fipscanister.o.sha1
├── fips_premain.c
└── fips_premain.c.sha1
4 directories, 32 files
[064bit-prompt]>
[064bit-prompt]> echo ${OPENSSL_DIR}
/home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16-static
[064bit-prompt]> tree ${OPENSSL_DIR}/bin ${OPENSSL_DIR}/lib
/home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16-static/bin
├── c_rehash
└── openssl
/home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16-static/lib
├── engines
├── libcrypto.a
├── libssl.a
└── pkgconfig
├── libcrypto.pc
├── libssl.pc
└── openssl.pc
2 directories, 7 files
[064bit-prompt]>
[064bit-prompt]> ldd ${OPENSSL_DIR}/bin/openssl
linux-vdso.so.1 => (0x00007ffeec045000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f12c19c2000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f12c15f8000)
/lib64/ld-linux-x86-64.so.2 (0x00007f12c1bc6000)
[064bit-prompt]>
[064bit-prompt]> ${OPENSSL_DIR}/bin/openssl version
OpenSSL 1.0.2t-fips 10 Sep 2019
[064bit-prompt]> ${OPENSSL_DIR}/bin/openssl sha1 ./code00.py
SHA1(./code00.py)= ff122260b025103dbc03316e3d3e26cd683e7a12
[064bit-prompt]> ${OPENSSL_DIR}/bin/openssl md5 ./code00.py
MD5(./code00.py)= eac85e46734260c1bfcceb89d6a3bd32
[064bit-prompt]> OPENSSL_FIPS=1 ${OPENSSL_DIR}/bin/openssl sha1 ./code00.py
SHA1(./code00.py)= ff122260b025103dbc03316e3d3e26cd683e7a12
[064bit-prompt]> OPENSSL_FIPS=1 ${OPENSSL_DIR}/bin/openssl md5 ./code00.py
Error setting digest md5
140584610875032:error:060A80A3:digital envelope routines:FIPS_DIGESTINIT:disabled for fips:fips_md.c:180:
3。 密码学模块
[064bit-prompt]> ls
code00.py cryptography-2.7.tar.gz
[064bit-prompt]> mkdir build
[064bit-prompt]> cd build
[064bit-prompt]>
[064bit-prompt]> CFLAGS="-I${OPENSSL_DIR}/include -DOPENSSL_FIPS=1" LDFLAGS="-L${OPENSSL_DIR}/lib" python3 -m pip wheel --no-cache --no-binary :all: ../cryptography-2.7.tar.gz
Processing /home/cfati/Work/Dev/Whosebug/q058228435/cryptography-2.7.tar.gz
Installing build dependencies ... done
Getting requirements to build wheel ... done
Preparing wheel metadata ... done
Collecting asn1crypto>=0.21.0 (from cryptography==2.7)
Downloading https://files.pythonhosted.org/packages/d1/e2/c518f2bc5805668803ebf0659628b0e9d77ca981308c7e9e5564b30b8337/asn1crypto-1.0.1.tar.gz (115kB)
|████████████████████████████████| 122kB 801kB/s
Collecting cffi!=1.11.3,>=1.8 (from cryptography==2.7)
Downloading https://files.pythonhosted.org/packages/93/1a/ab8c62b5838722f29f3daffcc8d4bd61844aa9b5f437341cc890ceee483b/cffi-1.12.3.tar.gz (456kB)
|████████████████████████████████| 460kB 1.8MB/s
Collecting six>=1.4.1 (from cryptography==2.7)
Downloading https://files.pythonhosted.org/packages/dd/bf/4138e7bfb757de47d1f4b6994648ec67a51efe58fa907c1e11e350cddfca/six-1.12.0.tar.gz
Collecting pycparser (from cffi!=1.11.3,>=1.8->cryptography==2.7)
Downloading https://files.pythonhosted.org/packages/68/9e/49196946aee219aead1290e00d1e7fdeab8567783e83e1b9ab5585e6206a/pycparser-2.19.tar.gz (158kB)
|████████████████████████████████| 163kB 4.5MB/s
Building wheels for collected packages: cryptography, asn1crypto, cffi, six, pycparser
Building wheel for cryptography (PEP 517) ... done
Stored in directory: /home/cfati/Work/Dev/Whosebug/q058228435/build
Building wheel for asn1crypto (setup.py) ... done
Stored in directory: /home/cfati/Work/Dev/Whosebug/q058228435/build
Building wheel for cffi (setup.py) ... done
Stored in directory: /home/cfati/Work/Dev/Whosebug/q058228435/build
Building wheel for six (setup.py) ... done
Stored in directory: /home/cfati/Work/Dev/Whosebug/q058228435/build
Building wheel for pycparser (setup.py) ... done
Stored in directory: /home/cfati/Work/Dev/Whosebug/q058228435/build
Successfully built cryptography asn1crypto cffi six pycparser
WARNING: You are using pip version 19.1.1, however version 19.2.3 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
[064bit-prompt]>
[064bit-prompt]> ls
asn1crypto-1.0.1-py3-none-any.whl cryptography-2.7-cp35-cp35m-linux_x86_64.whl six-1.12.0-py2.py3-none-any.whl
cffi-1.12.3-cp35-cp35m-linux_x86_64.whl pycparser-2.19-py2.py3-none-any.whl
[064bit-prompt]>
[064bit-prompt]> for f in $(ls *.whl); do unzip ${f} > /dev/null; done
[064bit-prompt]> ls
asn1crypto cffi-1.12.3-cp35-cp35m-linux_x86_64.whl cryptography-2.7-cp35-cp35m-linux_x86_64.whl pycparser-2.19-py2.py3-none-any.whl
asn1crypto-1.0.1.dist-info cffi-1.12.3.dist-info cryptography-2.7.dist-info six-1.12.0.dist-info
asn1crypto-1.0.1-py3-none-any.whl _cffi_backend.cpython-35m-x86_64-linux-gnu.so pycparser six-1.12.0-py2.py3-none-any.whl
cffi cryptography pycparser-2.19.dist-info six.py
[064bit-prompt]> PYTHONPATH=.:${PYTHONPATH} python3 ../code00.py
Python 3.5.2 (default, Jul 10 2019, 11:58:48) [GCC 5.4.0 20160609] 64bit on linux
OpenSSL version: OpenSSL 1.0.2t-fips 10 Sep 2019
FIPS_mode(): 0
FIPS_mode_set(1): 0
FIPS_mode(): 0
error:[755413103]:[FIPS routines]:[FIPS_check_incore_fingerprint]:[fingerprint does not match]
Done.
如你所见,我和你差不多。
4。深入研究
经过长时间(有些人可能认为是痛苦的)调试、试验...,我得出了一个结论。考虑到:
- 篡改来自 FOM 的任何内容(${FIPSDIR} 的内容),没有人会成为 FIPS 验证。坦率地说,这也不是,因为有具体说明在构建 FOM 时,只有 sys admin 应该将工件复制到安全位置...., bla, bla, bla.这对我来说似乎很偏执,但这些都是事实。顺便说一下,早在 2013 年,当我们 1st 接触到 FIPS(可能是为了防止任何可能的攻击(例如 MITM)), FOM 来源 CD 从 美国到俄罗斯:)))
- 默认 Python 版本是静态构建的(再次:)),这意味着 ${PYTHONCORE}(Python 解释器)驻留在 python 可执行文件中,而不是 .so (libpython*.so*) 可以 linked 到(并且 python 可执行文件在共享构建的情况下执行)
- Cryptography 的 _openssl 扩展模块(_openssl.abi*.so ) 需要来自 ${PYTHONCORE} 的符号(例如 PyLong_FromLong),但那是 OK因为此刻它将被加载到 (Python) 进程中(从上述可执行文件启动),它会找到它们(这是 [=81 上的常见做法) =]尼克斯)
- 构建步骤#3.2.:可执行文件(FPD - 必须运行) 找不到符号,所以失败
这是一个死锁(无论一个约束留下什么空间,都会被其他约束关闭),所以它根本无法完成!!!(至少,在这个时候). 句号!!! X(
5。备选方案
我打算将其作为一个优雅的替代方案(包括 OpenSSL .sos(与任何 (.so) rpath 设置为“那里”的客户端)在 .whl 中,在 旁边_openssl.abi3.so 对他们来说 link),但显然这是唯一的方法(至少我已经找到了)。
第1st步,是构建一个共享的OpenSSL版本(FOME将是 libcrypto.so.*).
[064bit-prompt]> ls
code00.py cryptography-2.7.tar.gz
[064bit-prompt]> export OPENSSL_DIR=/home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16
[064bit-prompt]> ldd ${OPENSSL_DIR}/bin/openssl
linux-vdso.so.1 => (0x00007ffe62faf000)
libssl.so.1.0.0 => /home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16/lib/libssl.so.1.0.0 (0x00007fe33c06f000)
libcrypto.so.1.0.0 => /home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16/lib/libcrypto.so.1.0.0 (0x00007fe33bb92000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe33b7c8000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fe33b5c4000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe33c2e3000)
[064bit-prompt]>
[064bit-prompt]> ${OPENSSL_DIR}/bin/openssl version
OpenSSL 1.0.2t-fips 10 Sep 2019
[064bit-prompt]> ${OPENSSL_DIR}/bin/openssl md5 ./code00.py
MD5(./code00.py)= eac85e46734260c1bfcceb89d6a3bd32
[064bit-prompt]> OPENSSL_FIPS=1 ${OPENSSL_DIR}/bin/openssl md5 ./code00.py
Error setting digest md5
139796140275352:error:060A80A3:digital envelope routines:FIPS_DIGESTINIT:disabled for fips:fips_md.c:180
在又一次深潜(多次失败的尝试)之后,我终于让它工作了。然而,采取了很多行动:
- 人工干预
- 黑客
- (蹩脚)解决方法(gainarii)
我担心如果我把所有东西都放在这里它会超过 30K chars 限制([SE.Meta]: Knowing Your Limits: What is the maximum length of a question title, post, image and links used?).
但是,我在 [GitHub]: CristiFati/Prebuilt-Binaries - (master) Prebuilt-Binaries/Cryptography/v2.7 上发布了 .whl。到目前为止,它仅适用于 Python 3.5 (64bit),因为它是 [= 上的默认版本539=]16。如果你使用另一个(更新的)版本,请告诉我,我会得到它(也许自己构建),并为该版本构建 .whl (我要去无论如何都要这样做)。
将原来的.whl换成我自己建的后:
[cfati@cfati-ubtu16x64-0:~/Work/Dev/Whosebug/q058228435/build]> ll
total 2936
drwxrwxr-x 2 cfati cfati 4096 Oct 9 21:40 .
drwxrwxr-x 4 cfati cfati 4096 Oct 9 21:28 ..
-rw-rw-r-- 1 cfati cfati 108067 Oct 9 08:43 asn1crypto-1.0.1-py3-none-any.whl
-rw-rw-r-- 1 cfati cfati 318045 Oct 9 08:43 cffi-1.12.3-cp35-cp35m-linux_x86_64.whl
-rw-rw-r-- 1 cfati cfati 2438739 Oct 9 21:40 cryptography-2.7-cp35-cp35m-linux_x86_64.whl
-rw-rw-r-- 1 cfati cfati 112066 Oct 9 08:43 pycparser-2.19-py2.py3-none-any.whl
-rw-rw-r-- 1 cfati cfati 12099 Oct 9 08:43 six-1.12.0-py2.py3-none-any.whl
[cfati@cfati-ubtu16x64-0:~/Work/Dev/Whosebug/q058228435/build]> for f in $(ls *.whl); do unzip ${f} > /dev/null; done
[cfati@cfati-ubtu16x64-0:~/Work/Dev/Whosebug/q058228435/build]> ls
asn1crypto cffi-1.12.3-cp35-cp35m-linux_x86_64.whl cryptography-2.7-cp35-cp35m-linux_x86_64.whl pycparser-2.19-py2.py3-none-any.whl
asn1crypto-1.0.1.dist-info cffi-1.12.3.dist-info cryptography-2.7.dist-info six-1.12.0.dist-info
asn1crypto-1.0.1-py3-none-any.whl _cffi_backend.cpython-35m-x86_64-linux-gnu.so pycparser six-1.12.0-py2.py3-none-any.whl
cffi cryptography pycparser-2.19.dist-info six.py
[cfati@cfati-ubtu16x64-0:~/Work/Dev/Whosebug/q058228435/build]> PYTHONPATH=.:${PYTHONPATH} python3 ../code00.py
Python 3.5.2 (default, Jul 10 2019, 11:58:48) [GCC 5.4.0 20160609] 64bit on linux
OpenSSL version: OpenSSL 1.0.2t-fips 10 Sep 2019
FIPS_mode(): 0
FIPS_mode_set(1): 1
FIPS_mode(): 1
Success !!!
Done.
相关(或多或少)posts:
- [SO]: Unable to build a working FIPS capable OpenSSL on HP-UX
根据 Python 密码学库的文档 [1],可以使用静态链接的 OpenSSL 构建自定义密码学轮。我尝试使用使用 FIPS 对象模块构建的 OpenSSL 安装来执行此操作,并且能够成功构建轮子,但发现它没有 FIPS 功能(无法设置 FIPS_mode_set=1)。
我创建了一个可以重现相同结果的 Dockerfile。最后的 Python 代码应该显示“1”和 "OpenSSL 1.0.2t-fips 10 Sep 2019" 而不是显示“0”和 "OpenSSL 1.0.2t 10 Sep 2019"(没有 -fips
指定)。
让我感到困惑的是,当我调用我构建的 openssl version
CLI 时,它正确地显示了带有 -fips
后缀的版本。因为,我猜我在构建密码学的某个地方出错了。
在此感谢任何帮助!
FROM centos
# Install build dependencies
RUN yum groupinstall -y "Development Tools" && \
yum install -y python-devel libffi-devel
# Install Python dependencies
RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
python get-pip.py && \
pip install virtualenv setuptools wheel pip
# Build Fips object module
RUN curl -O https://www.openssl.org/source/openssl-fips-2.0.16.tar.gz && \
tar xvf openssl-fips-2.0.16.tar.gz && \
cd openssl-fips-2.0.16 && \
./config && \
make && \
make install
# Build OpenSSL
RUN curl -O https://www.openssl.org/source/openssl-1.0.2t.tar.gz && \
tar xvf openssl-1.0.2t.tar.gz && \
cd /openssl-1.0.2t && \
./config fips no-shared -fPIC --prefix=/openssl-1.0.2t/openssl && \
make depend && \
make && \
make install_sw
# Build cryptography
RUN CFLAGS="-I/openssl-1.0.2t/openssl/include" LDFLAGS="-L/openssl-1.0.2t/openssl/lib" pip wheel --no-cache --no-binary :all: cryptography && \
pip install cryptography*.whl
# Test if fips is enabled
RUN python -c "\
from cryptography.hazmat.backends.openssl.backend import backend;\
print backend._lib.FIPS_mode_set(1);\
print ''.join([backend._lib.OPENSSL_VERSION_TEXT[i] for i in range(30)])"
[1] https://cryptography.io/en/latest/installation/#static-wheels
编辑: 通过将 -DOPENSSL_FIPS
添加到加密构建中,我能够使 OPENSSL_VERSION_TEXT
的输出变为 OpenSSL 1.0.2t-fips 10 Sep 20
但输出FIPS_mode_set(1) 仍然是 0。
编辑 2: 使用 ERR_get_error()
显示以下内容:
>>> print backend._lib.FIPS_mode_set(1)
0
>>> print backend._lib.ERR_get_error()
755413103
当我将其放入 openssl errstr
时,我得到:
openssl errstr 755413103
error:755413103:lib(85):func(1043):reason(259)
根据一些 Google 搜索,这表明指纹不匹配 (FIPS_R_FINGERPRINT_DOES_NOT_MATCH
)。虽然不确定从这里去哪里。
1。一般
首先,我想提一下,虽然我理解原因,但我并不完全同意 Cryptography 的观点 ([Cryptography]: Installation - Static Wheels)。共享库存在了几十年,并证明了它们的优越性。更不用说 Python 运送 2(标准)模块(_ssl 和 _hashlib) 动态 link 到 OpenSSL (无论它在系统上是什么)。作为旁注,在 Win 上,2 个 Python 模块也用于 link 静态 OpenSSL,但从 v3.7 开始,他们不再这样做了。回到 Nix:2 个 OpenSSL 版本被加载到同一个 (Python) 进程中.好像没什么害处,反而看起来好笑。正如今天的情况 (191009),v2.7[=296= 有一堆 .whls ] 和 v3.4,但是 none 对于相当不错的 (Python) 环境:
我记得前段时间类似的情况:除了2个标准模块外,还使用了M2Crypto。在那种情况下(我们完全运送 Python),2 个特定的(FIPS 支持)OpenSSL (dynamic) libs 也已发货,所有 Python 模块link 告诉他们。它适用于各种(桌面)环境(其中有许多“异国情调”的环境):
- CPU 架构(LE / BE):x86, AMD64, IA64, SPARC, PPC, zSeries
- OSes(有多个版本):Win、Lnx (RH,CentOS,OEL,SuSE, Xen, Ubuntu), Solaris, AIX, HP-UX(作为个人练习,我添加了OSX)
[OpenSSL]: UserGuide-2.0.pdf - User Guide for the OpenSSL FIPS Object Module v2.0 (referenced from [OpenSSL]: FIPS-140(如果 URL 发生变化)包含所有需要的详细信息。
在继续之前,这里有一些我将在 post 中使用的术语:
- FOM - FIPS 对象模块 (fipscanister.o)
- FOME - 可执行文件 (ELF (PE on Win )) FOM 是 linked in。请记住,它可以是可执行文件 本身 ,也可以是 .so (.dll).此外,如果它包含在静态库 (.a) 中,则不会 linked(只是存档)。作为旁注,当 OpenSSL 构建共享时,FOME 是 libcrypto.so.* ,而当静态构建时(如本例),它是 link 与 libcrypto.a 的可执行文件(例如 openssl可执行)
FOM 位于 OpenSSL 之上(可能还有其他类似的加密提供程序,例如 LibreSSL, WolfSSL, ),它旨在通过限制一些原本可用的功能来加强安全性(根据 NIST 标准)。一个这样的功能示例是 md5 哈希的使用,它被认为是弱的(我很确定 sha1 也将在即将发布的下一个版本中跟进。
这是(在 运行 时间)发生的事情的(非常简化的)版本:
- FIPS 模式开启:
- 检查是否满足附加约束:
- 是:继续(使用默认功能)
- 否:Return 有错误
- 检查是否满足附加约束:
- FIPS 模式关闭:
- 回退到默认功能
#1.1.部分为自测。其中包括:
- 计算 FOME 签名
- 将它与一个值(也存储在 FOME 中)进行比较
这恰好确保(或大大减少机会)没有人篡改(手动修改、反汇编,...)FOME。为了更好地理解签名机制,让我们深入了解 FOME 构建过程:
- 所有 FOME 来源 + FOM 都被编译(成目标文件)
- 它们被 link 编在一起(进入 FOME)。这是正常构建结束的时候
- 正在计算FOME签名
- 来自 #1. + fips_premain.o 的项目正在 link 编辑成(真实的,非.dll) 可执行文件 (FPD) 针对 FOME (#1.) 调用
- FPD。它读取 FOME 的 .rodata 部分并计算其 sha1 哈希。请注意,它会忽略位于特定地址41 字节区域(打孔)
重复 - #3.1.,但这次 fips_premain.o 被重新编译以包含上一步的哈希值。现在可以清楚地看到上一步的打孔,它是签名所在的位置:(length of sha hash (40) + nul)。 这是最终的FOME
注意:在 Win 上,情况略有不同。
我已经成功地重现了这个问题。我将从测试脚本开始。
code00.py:
#!/usr/bin/env python3
import sys
from cryptography.hazmat.backends.openssl.backend import backend
import cffi
def main():
ffi = cffi.FFI()
lib = backend._lib
fmt = "OpenSSL version: {0:s}\nFIPS_mode(): {1:d}\nFIPS_mode_set(1): {2:d}\nFIPS_mode(): {3:d}"
print(fmt.format(ffi.string(lib.OPENSSL_VERSION_TEXT).decode(),
lib.FIPS_mode(), lib.FIPS_mode_set(1), lib.FIPS_mode()))
err = lib.ERR_get_error()
if err:
err_fmt = "error:[{0:d}]:[{1:s}]:[{2:s}]:[{3:s}]"
print(err_fmt.format(
err,
ffi.string(lib.ERR_lib_error_string(err)).decode(),
ffi.string(lib.ERR_func_error_string(err)).decode(),
ffi.string(lib.ERR_reason_error_string(err)).decode()
))
else:
print("Success !!!")
if __name__ == "__main__":
print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main()
print("\nDone.")
2。设置
我已经构建了 FOM 和 FIPS 能力 OpenSSL(类似于你的,但我定制了他们的路径)。在构建 FOM 和 OpenSSL.
时使用了 ${FIPSDIR} 变量[cfati@cfati-ubtu16x64-0:~/Work/Dev/Whosebug/q058228435]> ~/sopr.sh *** Set shorter prompt to better fit when pasted in Whosebug (or other) pages *** [064bit-prompt]> uname -a Linux cfati-ubtu16x64-0 4.15.0-65-generic #74~16.04.1-Ubuntu SMP Wed Sep 18 09:51:44 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux [064bit-prompt]> cat /etc/lsb-release | grep DESCR DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS" [064bit-prompt]> gcc --version | grep gcc gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609 [064bit-prompt]> [064bit-prompt]> echo ${FIPSDIR} /home/cfati/Work/Dev/Tools/zzz_Build/OpenSSL/int/openssl-fips-2.0.16 [064bit-prompt]> tree ${FIPSDIR} /home/cfati/Work/Dev/Tools/zzz_Build/OpenSSL/int/openssl-fips-2.0.16 ├── bin │ ├── fipsld │ └── fips_standalone_sha1 ├── include │ └── openssl │ ├── aes.h │ ├── bn.h │ ├── buffer.h │ ├── cmac.h │ ├── crypto.h │ ├── des.h │ ├── des_old.h │ ├── dh.h │ ├── dsa.h │ ├── ebcdic.h │ ├── ecdh.h │ ├── ecdsa.h │ ├── ec.h │ ├── e_os2.h │ ├── evp.h │ ├── fips.h │ ├── fips_rand.h │ ├── fipssyms.h │ ├── hmac.h │ ├── modes.h │ ├── opensslconf.h │ ├── opensslv.h │ ├── ossl_typ.h │ ├── rsa.h │ ├── sha.h │ └── symhacks.h └── lib ├── fipscanister.o ├── fipscanister.o.sha1 ├── fips_premain.c └── fips_premain.c.sha1 4 directories, 32 files [064bit-prompt]> [064bit-prompt]> echo ${OPENSSL_DIR} /home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16-static [064bit-prompt]> tree ${OPENSSL_DIR}/bin ${OPENSSL_DIR}/lib /home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16-static/bin ├── c_rehash └── openssl /home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16-static/lib ├── engines ├── libcrypto.a ├── libssl.a └── pkgconfig ├── libcrypto.pc ├── libssl.pc └── openssl.pc 2 directories, 7 files [064bit-prompt]> [064bit-prompt]> ldd ${OPENSSL_DIR}/bin/openssl linux-vdso.so.1 => (0x00007ffeec045000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f12c19c2000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f12c15f8000) /lib64/ld-linux-x86-64.so.2 (0x00007f12c1bc6000) [064bit-prompt]> [064bit-prompt]> ${OPENSSL_DIR}/bin/openssl version OpenSSL 1.0.2t-fips 10 Sep 2019 [064bit-prompt]> ${OPENSSL_DIR}/bin/openssl sha1 ./code00.py SHA1(./code00.py)= ff122260b025103dbc03316e3d3e26cd683e7a12 [064bit-prompt]> ${OPENSSL_DIR}/bin/openssl md5 ./code00.py MD5(./code00.py)= eac85e46734260c1bfcceb89d6a3bd32 [064bit-prompt]> OPENSSL_FIPS=1 ${OPENSSL_DIR}/bin/openssl sha1 ./code00.py SHA1(./code00.py)= ff122260b025103dbc03316e3d3e26cd683e7a12 [064bit-prompt]> OPENSSL_FIPS=1 ${OPENSSL_DIR}/bin/openssl md5 ./code00.py Error setting digest md5 140584610875032:error:060A80A3:digital envelope routines:FIPS_DIGESTINIT:disabled for fips:fips_md.c:180:
3。 密码学模块
[064bit-prompt]> ls code00.py cryptography-2.7.tar.gz [064bit-prompt]> mkdir build [064bit-prompt]> cd build [064bit-prompt]> [064bit-prompt]> CFLAGS="-I${OPENSSL_DIR}/include -DOPENSSL_FIPS=1" LDFLAGS="-L${OPENSSL_DIR}/lib" python3 -m pip wheel --no-cache --no-binary :all: ../cryptography-2.7.tar.gz Processing /home/cfati/Work/Dev/Whosebug/q058228435/cryptography-2.7.tar.gz Installing build dependencies ... done Getting requirements to build wheel ... done Preparing wheel metadata ... done Collecting asn1crypto>=0.21.0 (from cryptography==2.7) Downloading https://files.pythonhosted.org/packages/d1/e2/c518f2bc5805668803ebf0659628b0e9d77ca981308c7e9e5564b30b8337/asn1crypto-1.0.1.tar.gz (115kB) |████████████████████████████████| 122kB 801kB/s Collecting cffi!=1.11.3,>=1.8 (from cryptography==2.7) Downloading https://files.pythonhosted.org/packages/93/1a/ab8c62b5838722f29f3daffcc8d4bd61844aa9b5f437341cc890ceee483b/cffi-1.12.3.tar.gz (456kB) |████████████████████████████████| 460kB 1.8MB/s Collecting six>=1.4.1 (from cryptography==2.7) Downloading https://files.pythonhosted.org/packages/dd/bf/4138e7bfb757de47d1f4b6994648ec67a51efe58fa907c1e11e350cddfca/six-1.12.0.tar.gz Collecting pycparser (from cffi!=1.11.3,>=1.8->cryptography==2.7) Downloading https://files.pythonhosted.org/packages/68/9e/49196946aee219aead1290e00d1e7fdeab8567783e83e1b9ab5585e6206a/pycparser-2.19.tar.gz (158kB) |████████████████████████████████| 163kB 4.5MB/s Building wheels for collected packages: cryptography, asn1crypto, cffi, six, pycparser Building wheel for cryptography (PEP 517) ... done Stored in directory: /home/cfati/Work/Dev/Whosebug/q058228435/build Building wheel for asn1crypto (setup.py) ... done Stored in directory: /home/cfati/Work/Dev/Whosebug/q058228435/build Building wheel for cffi (setup.py) ... done Stored in directory: /home/cfati/Work/Dev/Whosebug/q058228435/build Building wheel for six (setup.py) ... done Stored in directory: /home/cfati/Work/Dev/Whosebug/q058228435/build Building wheel for pycparser (setup.py) ... done Stored in directory: /home/cfati/Work/Dev/Whosebug/q058228435/build Successfully built cryptography asn1crypto cffi six pycparser WARNING: You are using pip version 19.1.1, however version 19.2.3 is available. You should consider upgrading via the 'pip install --upgrade pip' command. [064bit-prompt]> [064bit-prompt]> ls asn1crypto-1.0.1-py3-none-any.whl cryptography-2.7-cp35-cp35m-linux_x86_64.whl six-1.12.0-py2.py3-none-any.whl cffi-1.12.3-cp35-cp35m-linux_x86_64.whl pycparser-2.19-py2.py3-none-any.whl [064bit-prompt]> [064bit-prompt]> for f in $(ls *.whl); do unzip ${f} > /dev/null; done [064bit-prompt]> ls asn1crypto cffi-1.12.3-cp35-cp35m-linux_x86_64.whl cryptography-2.7-cp35-cp35m-linux_x86_64.whl pycparser-2.19-py2.py3-none-any.whl asn1crypto-1.0.1.dist-info cffi-1.12.3.dist-info cryptography-2.7.dist-info six-1.12.0.dist-info asn1crypto-1.0.1-py3-none-any.whl _cffi_backend.cpython-35m-x86_64-linux-gnu.so pycparser six-1.12.0-py2.py3-none-any.whl cffi cryptography pycparser-2.19.dist-info six.py [064bit-prompt]> PYTHONPATH=.:${PYTHONPATH} python3 ../code00.py Python 3.5.2 (default, Jul 10 2019, 11:58:48) [GCC 5.4.0 20160609] 64bit on linux OpenSSL version: OpenSSL 1.0.2t-fips 10 Sep 2019 FIPS_mode(): 0 FIPS_mode_set(1): 0 FIPS_mode(): 0 error:[755413103]:[FIPS routines]:[FIPS_check_incore_fingerprint]:[fingerprint does not match] Done.
如你所见,我和你差不多。
4。深入研究
经过长时间(有些人可能认为是痛苦的)调试、试验...,我得出了一个结论。考虑到:
- 篡改来自 FOM 的任何内容(${FIPSDIR} 的内容),没有人会成为 FIPS 验证。坦率地说,这也不是,因为有具体说明在构建 FOM 时,只有 sys admin 应该将工件复制到安全位置...., bla, bla, bla.这对我来说似乎很偏执,但这些都是事实。顺便说一下,早在 2013 年,当我们 1st 接触到 FIPS(可能是为了防止任何可能的攻击(例如 MITM)), FOM 来源 CD 从 美国到俄罗斯:)))
- 默认 Python 版本是静态构建的(再次:)),这意味着 ${PYTHONCORE}(Python 解释器)驻留在 python 可执行文件中,而不是 .so (libpython*.so*) 可以 linked 到(并且 python 可执行文件在共享构建的情况下执行)
- Cryptography 的 _openssl 扩展模块(_openssl.abi*.so ) 需要来自 ${PYTHONCORE} 的符号(例如 PyLong_FromLong),但那是 OK因为此刻它将被加载到 (Python) 进程中(从上述可执行文件启动),它会找到它们(这是 [=81 上的常见做法) =]尼克斯)
- 构建步骤#3.2.:可执行文件(FPD - 必须运行) 找不到符号,所以失败
这是一个死锁(无论一个约束留下什么空间,都会被其他约束关闭),所以它根本无法完成!!!(至少,在这个时候). 句号!!! X(
5。备选方案
我打算将其作为一个优雅的替代方案(包括 OpenSSL .sos(与任何 (.so) rpath 设置为“那里”的客户端)在 .whl 中,在 旁边_openssl.abi3.so 对他们来说 link),但显然这是唯一的方法(至少我已经找到了)。
第1st步,是构建一个共享的OpenSSL版本(FOME将是 libcrypto.so.*).
[064bit-prompt]> ls code00.py cryptography-2.7.tar.gz [064bit-prompt]> export OPENSSL_DIR=/home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16 [064bit-prompt]> ldd ${OPENSSL_DIR}/bin/openssl linux-vdso.so.1 => (0x00007ffe62faf000) libssl.so.1.0.0 => /home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16/lib/libssl.so.1.0.0 (0x00007fe33c06f000) libcrypto.so.1.0.0 => /home/cfati/Work/Dev/Tools/openssl-1.0.2t-fips-2.0.16/lib/libcrypto.so.1.0.0 (0x00007fe33bb92000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe33b7c8000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fe33b5c4000) /lib64/ld-linux-x86-64.so.2 (0x00007fe33c2e3000) [064bit-prompt]> [064bit-prompt]> ${OPENSSL_DIR}/bin/openssl version OpenSSL 1.0.2t-fips 10 Sep 2019 [064bit-prompt]> ${OPENSSL_DIR}/bin/openssl md5 ./code00.py MD5(./code00.py)= eac85e46734260c1bfcceb89d6a3bd32 [064bit-prompt]> OPENSSL_FIPS=1 ${OPENSSL_DIR}/bin/openssl md5 ./code00.py Error setting digest md5 139796140275352:error:060A80A3:digital envelope routines:FIPS_DIGESTINIT:disabled for fips:fips_md.c:180
在又一次深潜(多次失败的尝试)之后,我终于让它工作了。然而,采取了很多行动:
- 人工干预
- 黑客
- (蹩脚)解决方法(gainarii)
我担心如果我把所有东西都放在这里它会超过 30K chars 限制([SE.Meta]: Knowing Your Limits: What is the maximum length of a question title, post, image and links used?).
但是,我在 [GitHub]: CristiFati/Prebuilt-Binaries - (master) Prebuilt-Binaries/Cryptography/v2.7 上发布了 .whl。到目前为止,它仅适用于 Python 3.5 (64bit),因为它是 [= 上的默认版本539=]16。如果你使用另一个(更新的)版本,请告诉我,我会得到它(也许自己构建),并为该版本构建 .whl (我要去无论如何都要这样做)。
将原来的.whl换成我自己建的后:
[cfati@cfati-ubtu16x64-0:~/Work/Dev/Whosebug/q058228435/build]> ll total 2936 drwxrwxr-x 2 cfati cfati 4096 Oct 9 21:40 . drwxrwxr-x 4 cfati cfati 4096 Oct 9 21:28 .. -rw-rw-r-- 1 cfati cfati 108067 Oct 9 08:43 asn1crypto-1.0.1-py3-none-any.whl -rw-rw-r-- 1 cfati cfati 318045 Oct 9 08:43 cffi-1.12.3-cp35-cp35m-linux_x86_64.whl -rw-rw-r-- 1 cfati cfati 2438739 Oct 9 21:40 cryptography-2.7-cp35-cp35m-linux_x86_64.whl -rw-rw-r-- 1 cfati cfati 112066 Oct 9 08:43 pycparser-2.19-py2.py3-none-any.whl -rw-rw-r-- 1 cfati cfati 12099 Oct 9 08:43 six-1.12.0-py2.py3-none-any.whl [cfati@cfati-ubtu16x64-0:~/Work/Dev/Whosebug/q058228435/build]> for f in $(ls *.whl); do unzip ${f} > /dev/null; done [cfati@cfati-ubtu16x64-0:~/Work/Dev/Whosebug/q058228435/build]> ls asn1crypto cffi-1.12.3-cp35-cp35m-linux_x86_64.whl cryptography-2.7-cp35-cp35m-linux_x86_64.whl pycparser-2.19-py2.py3-none-any.whl asn1crypto-1.0.1.dist-info cffi-1.12.3.dist-info cryptography-2.7.dist-info six-1.12.0.dist-info asn1crypto-1.0.1-py3-none-any.whl _cffi_backend.cpython-35m-x86_64-linux-gnu.so pycparser six-1.12.0-py2.py3-none-any.whl cffi cryptography pycparser-2.19.dist-info six.py [cfati@cfati-ubtu16x64-0:~/Work/Dev/Whosebug/q058228435/build]> PYTHONPATH=.:${PYTHONPATH} python3 ../code00.py Python 3.5.2 (default, Jul 10 2019, 11:58:48) [GCC 5.4.0 20160609] 64bit on linux OpenSSL version: OpenSSL 1.0.2t-fips 10 Sep 2019 FIPS_mode(): 0 FIPS_mode_set(1): 1 FIPS_mode(): 1 Success !!! Done.
相关(或多或少)posts:
- [SO]: Unable to build a working FIPS capable OpenSSL on HP-UX