我正在尝试在Python的ssl模块中实现FIPS_mode和FIPS_mode_set函数,因为默认情况下不存在这些函数。由于各种使用原因,针对Python 3.4的A patch已提交并被拒绝。

以此补丁为灵感,我进行了一些修改,并在 ssl.py 中添加了以下代码:

try:
    from _ssl import FIPS_mode, FIPS_mode_set
except ImportError:
    pass

以及 _ssl.c 中的以下代码:
#define EXPORT_FIPSMODE_FUNCS

#ifdef EXPORT_FIPSMODE_FUNCS


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;
}

#endif  //EXPORT_FIPSMODE_FUNCS

/* List of functions exported by this module. */
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_SET_METHODDEF
    _SSL_FIPS_MODE_METHODDEF
    {NULL,                  NULL}            /* Sentinel */
};

但是,这将引发以下错误:
./Modules/_ssl.c:5060:5: error: '_SSL_FIPS_MODE_SET_METHODDEF' undeclared here (not in a function)
     _SSL_FIPS_MODE_SET_METHODDEF

./Modules/_ssl.c:5061:5: error: expected '}' before '_SSL_FIPS_MODE_METHODDEF'
     _SSL_FIPS_MODE_METHODDEF

./Modules/_ssl.c:4641:1: warning: '_ssl_FIPS_mode_impl' defined but not used [-Wunused-function]  _ssl_FIPS_mode_impl(PyObject
*module) {

./Modules/_ssl.c:4646:1: warning: '_ssl_FIPS_mode_set_impl' defined but not used [-Wunused-function]
_ssl_FIPS_mode_set_impl(PyObject *module, int n) {  ^

我很确定我在这里错过了一些非常琐碎的事情,但是我似乎无法弄清楚到底是什么。任何帮助,将不胜感激!谢谢!

更新:

@CristiFati 大喊大叫,他指出我缺少需要定义的宏,因此我能够解决此问题。如果其他人需要在Python 3.6中实现FIPS模式,请添加以下代码:

_ssl.c:
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 */
};

_ssl.c.h:
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;
}

ssl.py:
try:
    from _ssl import FIPS_mode, FIPS_mode_set
    print('successful import')
except ImportError as e:
    print('error in importing')
    print(e)

最佳答案

在查看源代码时,我注意到与 3.4相比,自Python 3.5 以来,$ {PYTHON_SRC_DIR}/Modules/_ssl.c结构发生了变化(“稍微有点”结构化并且还包含生成的代码)我将修补程序提交到的版本( [Python.Bugs]: FIPS_mode() and FIPS_mode_set() functions in Python (ssl) )。基本上,PySSL_methods数组中的每个条目都不再当场定义,而是通过预处理器宏间接定义。您是根据新标准采取行动的,但是却忘记了定义宏。

为了解决问题:

  • 为PyMethodDef条目定义2个宏( #ifdef EXPORT_FIPSMODE_FUNCS中的):
     // ...
    
     }
    
     #define _SSL_FIPS_MODE_METHODDEF    \
         {"FIPS_mode", (PyCFunction)_ssl_FIPS_mode_impl, METH_NOARGS, NULL},
    
     #define _SSL_FIPS_MODE_SET_METHODDEF    \
         {"FIPS_mode_set", (PyCFunction)_ssl_FIPS_mode_set_impl, METH_O, NULL},
    
     #endif  // EXPORT_FIPSMODE_FUNCS
    
     // ...
    
  • 在定义PySSL_method时通过 #ifdef 子句(类似于它们的定义位置)来“保护”新宏(这样,如果未定义EXPORT_FIPSMODE_FUNCS宏,则不会出现编译错误):
     // ...
    
     _SSL_NID2OBJ_METHODDEF
     #ifdef EXPORT_FIPSMODE_FUNCS
     _SSL_FIPS_MODE_METHODDEF
     _SSL_FIPS_MODE_SET_METHODDEF
     #endif  // EXPORT_FIPSMODE_FUNCS
     {NULL,                  NULL}            /* Sentinel */
    
     // ...
    

  • 注释:
  • 因为您没有定义文档字符串,所以我用NULL(PyMethodDef的最后一个成员)填充了它们。这将导致语法正确的代码,但是当您在这两个函数中的任何一个上运行help()时,我不确定它在运行时的行为如何。如果出现段错误,请从补丁中复制定义,或使用PyDoc_STRVAR定义一些虚拟字符串(当然,还要重新编译)
  • 尽管它比原始补丁更接近于标准,但它还不尽如人意,如(2nd)编辑中所示的OP(@HussainAliAkbar)。事情要复杂一些(还涉及$ {PYTHON_SRC_DIR}/Modules/clinic/_ssl.c.h)。请参阅
  • 下的下一个更新部分

    更新#0
  • 我花了一些时间检查该诊所文件夹的处理情况。这是[Python.Docs]: Argument Clinic How-To。我认为这是的主要改进,因为它使开发人员不必在每个函数(“猴子工作”)
  • 中编写参数解析代码(涉及PyArg _ *,Py * _Check函数)。
  • 我花了几个小时,但我设法做到了“按书”(至少,我认为是)

    Python-3.6.4-ssl_fips.diff:
    --- Python-3.6.4/Modules/_ssl.c.orig    2018-07-27 19:10:06.131999999 +0300
    +++ Python-3.6.4/Modules/_ssl.c 2018-07-27 20:32:15.531999999 +0300
    @@ -4789,6 +4789,46 @@
         return result;
     }
    
    +#if defined(EXPORT_FIPSMODE_FUNCS)
    +
    +unsigned char _fips_table_sig[0x0C] = {0x21, 0x7A, 0x65, 0x6C, 0x75, 0x72, 0x20, 0x49, 0x54, 0x41, 0x46, 0x00};
    +
    +/*[clinic input]
    +_ssl.FIPS_mode
    +
    +Return 1 (!=0) if FIPS mode is enabled, 0 otherwise.
    +[clinic start generated code]*/
    +
    +static PyObject *
    +_ssl_FIPS_mode_impl(PyObject *module)
    +/*[clinic end generated code: output=89f5a88ec715a291 input=52e4e5fdd1f555c7]*/
    +{
    +    return PyLong_FromLong(FIPS_mode());
    +}
    +
    +/*[clinic input]
    +_ssl.FIPS_mode_set
    +    mode: int
    +    /
    +
    +Try to set the FIPS mode to 'mode' (int).
    +
    +Return nothing. Raise SSLError when enabling FIPS mode fails.
    +[clinic start generated code]*/
    +
    +static PyObject *
    +_ssl_FIPS_mode_set_impl(PyObject *module, int mode)
    +/*[clinic end generated code: output=70e3e9f3bb4fce65 input=899c21a986720235]*/
    +{
    +if (FIPS_mode_set(mode) == 0) {
    +        _setSSLError(ERR_error_string(ERR_get_error(), NULL), 0, __FILE__, __LINE__);
    +        return NULL;
    +    }
    +    Py_RETURN_NONE;
    +}
    +
    +#endif  // EXPORT_FIPSMODE_FUNCS
    +
     #ifdef _MSC_VER
    
     static PyObject*
    @@ -5055,6 +5095,8 @@
         _SSL_ENUM_CRLS_METHODDEF
         _SSL_TXT2OBJ_METHODDEF
         _SSL_NID2OBJ_METHODDEF
    +    _SSL_FIPS_MODE_METHODDEF
    +    _SSL_FIPS_MODE_SET_METHODDEF
         {NULL,                  NULL}            /* Sentinel */
     };
    
    --- Python-3.6.4/Modules/clinic/_ssl.c.h.orig   2018-07-27 19:10:48.067999999 +0300
    +++ Python-3.6.4/Modules/clinic/_ssl.c.h    2018-07-27 20:31:04.507999999 +0300
    @@ -1062,6 +1062,61 @@
         return return_value;
     }
    
    +#if defined(EXPORT_FIPSMODE_FUNCS)
    +
    +PyDoc_STRVAR(_ssl_FIPS_mode__doc__,
    +"FIPS_mode($module, /)\n"
    +"--\n"
    +"\n"
    +"Return 1 (!=0) if FIPS mode is enabled, 0 otherwise.");
    +
    +#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);
    +}
    +
    +#endif /* defined(EXPORT_FIPSMODE_FUNCS) */
    +
    +#if defined(EXPORT_FIPSMODE_FUNCS)
    +
    +PyDoc_STRVAR(_ssl_FIPS_mode_set__doc__,
    +"FIPS_mode_set($module, mode, /)\n"
    +"--\n"
    +"\n"
    +"Try to set the FIPS mode to \'mode\' (int).\n"
    +"\n"
    +"Return nothing. Raise SSLError when enabling FIPS mode fails.");
    +
    +#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 mode);
    +
    +static PyObject *
    +_ssl_FIPS_mode_set(PyObject *module, PyObject *arg)
    +{
    +    PyObject *return_value = NULL;
    +    int mode;
    +
    +    if (!PyArg_Parse(arg, "i:FIPS_mode_set", &mode)) {
    +        goto exit;
    +    }
    +    return_value = _ssl_FIPS_mode_set_impl(module, mode);
    +
    +exit:
    +    return return_value;
    +}
    +
    +#endif /* defined(EXPORT_FIPSMODE_FUNCS) */
    +
     #if defined(_MSC_VER)
    
     PyDoc_STRVAR(_ssl_enum_certificates__doc__,
    @@ -1161,6 +1216,14 @@
         #define _SSL_RAND_EGD_METHODDEF
     #endif /* !defined(_SSL_RAND_EGD_METHODDEF) */
    
    +#ifndef _SSL_FIPS_MODE_METHODDEF
    +    #define _SSL_FIPS_MODE_METHODDEF
    +#endif /* !defined(_SSL_FIPS_MODE_METHODDEF) */
    +
    +#ifndef _SSL_FIPS_MODE_SET_METHODDEF
    +    #define _SSL_FIPS_MODE_SET_METHODDEF
    +#endif /* !defined(_SSL_FIPS_MODE_SET_METHODDEF) */
    +
     #ifndef _SSL_ENUM_CERTIFICATES_METHODDEF
         #define _SSL_ENUM_CERTIFICATES_METHODDEF
     #endif /* !defined(_SSL_ENUM_CERTIFICATES_METHODDEF) */
    @@ -1168,4 +1231,4 @@
     #ifndef _SSL_ENUM_CRLS_METHODDEF
         #define _SSL_ENUM_CRLS_METHODDEF
     #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
    -/*[clinic end generated code: output=a8b184655068c238 input=a9049054013a1b77]*/
    +/*[clinic end generated code: output=94d0ec4213d44124 input=a9049054013a1b77]*/
    --- Python-3.6.4/Lib/ssl.py.orig    2018-07-27 19:10:29.827999999 +0300
    +++ Python-3.6.4/Lib/ssl.py 2018-03-28 23:30:35.065667344 +0300
    @@ -114,6 +114,11 @@
         # LibreSSL does not provide RAND_egd
         pass
    
    +try:
    +    from _ssl import FIPS_mode, FIPS_mode_set
    +except ImportError:
    +    # Compiled without FIPS functions support
    +    pass
    
     from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_TLSv1_3
     from _ssl import _OPENSSL_API_VERSION
    --- Python-3.6.4/setup.py.orig  2018-07-27 19:09:33.763999999 +0300
    +++ Python-3.6.4/setup.py   2018-03-28 23:30:35.061667315 +0300
    @@ -862,6 +862,7 @@
                 ssl_libs is not None):
                 exts.append( Extension('_ssl', ['_ssl.c'],
                                        include_dirs = ssl_incs,
    +                                   define_macros = [("EXPORT_FIPSMODE_FUNCS", None)],
                                        library_dirs = ssl_libs,
                                        libraries = ['ssl', 'crypto'],
                                        depends = ['socketmodule.h']), )
    
    注释:
  • 这是一个差异(请注意,它是$ {PYTHON_SRC_DIR} 之外的)。有关如何在Win上应用修补程序的信息,请参见[SO]: Run/Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? (@CristiFati's answer)(修补utrunner 部分)(基本上,以开头的每一行都以一个“+” 符号开头,而以开头的每一行都以一个“-” 符号熄灭) 。我正在使用Cygwin,顺便说一句
  • 源基线为 v3.6.4 (由文件名指出)
  • 4个文件被修改:
  • $ {PYTHON_SRC_DIR}/Modules/_ssl.c-包含手动更改和生成的(如您所猜测的,由Argument Clinic提供)更改(生成的片段易于从注释中发现)
  • $ {PYTHON_SRC_DIR}/Modules/clinic/_ssl.c.h-仅包含生成的代码
  • 其他2个包含手动更改的


  • 我还测试了更改(有一次我使用了为[SO]: How to enable FIPS mode for libcrypto and libssl packaged with Python? (@CristiFati's answer)构建的OpenSSL版本)
    输出:


    可能还会查看[SO]: OpenSSL FIPS_mode_set not working in Python cryptography library (@CristiFati's answer)

    10-06 00:59