c - 使用 OpenSSL C 库在多线程中生成椭圆曲线 key 对 (EC_KEY_generate_key)

标签 c multithreading openssl ecdsa

我想生成许多 ec key 对。为了稍微加快这个过程,我重写了我的应用程序以使用多个线程来完成这项工作。这是每个线程想要生成 key 的方式的代码片段:

(...)
EC_KEY* _ec_key = EC_KEY_new(); 
EC_GROUP* ec_group_new = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); 
const EC_GROUP* ec_group = ec_group_new; 
if (!EC_KEY_set_group(ec_key,ec_group)) 
  DieWithError("Error in initializeCrypto, EC_KEY_set_group failed!");

// Segfault at this position
if(!EC_KEY_generate_key(ec_key))
  DieWithError ("Error in generateKeys, EC_KEY_generate_key failed!");

(...)
EC_GROUP_free(ec_group_new); 
EC_KEY_free(ec_key);

乍一看,似乎一切正常。在我的 i5 520m 上使用四个线程,应用程序的运行速度提高了一倍。但是在 3-4 代 E6 key 生成之后,它突然出现了段错误。如果我锁定 EC_KEY_generate_key 操作,则不再有段错误,但使用多线程的优势就消失了。现在我的问题。是否可以在不破坏内存的情况下将 key 的创建拆分为多个线程?我没有使用谷歌找到任何信息。 SSL Docu不过,没有提及任何有关线程安全的内容。非常感谢任何帮助。谢谢

最佳答案

// Segfault at this position
if(!EC_KEY_generate_key(ec_key))
  DieWithError ("Error in generateKeys, EC_KEY_generate_key failed!");
...

... But then after 3-4 E6 key generations it suddenly segfaults.

您正在使用 OpenSSL 的随机数生成器,它不是线程安全的。下面是来自 cryptlib.c 的第 125 行。注意随机数生成器和椭圆曲线齿轮在列表中。

/* real #defines in crypto.h, keep these upto date */
static const char* const lock_names[CRYPTO_NUM_LOCKS] =
    {
    "<<ERROR>>",
    "err",
    "ex_data",
    "x509",
    "x509_info",
    "x509_pkey",
    "x509_crl",
    "x509_req",
    ...
    "ssl_ctx",
    "ssl_session",
    "ssl",
    "ssl_method",
    "rand",
    "rand2",
    ...
    "ecdsa",
    "ec",
    "ecdh",
    "bn",
    "ec_pre_comp",
    ...
    };

您必须明确设置锁。参见 OpenSSL's threads(3) .


Is it possible split the creation of keys into multiple threads without corrupting memory?

是的,但是你必须使用 OpenSSL 的锁定机制。

这是我在 C++ 中的 OpenSSL 初始化例程。它初始化锁并设置回调。

pthread_mutex_t s_locks[CRYPTO_NUM_LOCKS] = { };

void Initialize()
{    
    static once_flag init;
    std::call_once(init, []() {      

        // Standard OpenSSL library init
        OPENSSL_no_config();
        SSL_library_init();

        SSL_load_error_strings();
        OpenSSL_add_ssl_algorithms();

        // Lock setup
        LOCK_setup();
        CALLBACK_setup();
    });
}

void LOCK_setup()
{    
    ASSERT(CRYPTO_NUM_LOCKS == CRYPTO_num_locks());
    if(CRYPTO_NUM_LOCKS != CRYPTO_num_locks())
        throw runtime_error("CRYPTO_NUM_LOCKS mismatch");

    for(unsigned i = 0; i < CRYPTO_NUM_LOCKS; ++i)
    {
        int rc = pthread_mutex_init(&s_locks[i], NULL);
        ASSERT(rc == 0);
        if(!(rc == 0))
            throw runtime_error("pthread_mutex_init");
    }
}

void CALLBACK_setup()
{    
    CRYPTO_set_id_callback(&ThreadIdFnc);
    CRYPTO_set_locking_callback(&LockingFnc);
}

void LockingFnc(int mode, int idx, const char* file, int line)
{
    ASSERT(mode == CRYPTO_LOCK || mode == CRYPTO_UNLOCK);
    ASSERT(CRYPTO_NUM_LOCKS == CRYPTO_num_locks());
    ASSERT(idx >= 0 && idx < CRYPTO_NUM_LOCKS);

    if(!(idx >= 0 && idx < CRYPTO_NUM_LOCKS))
    {    
        ostringstream oss;
        oss << "LockingFnc: lock failed with bad index ";
        oss << idx << ". File: " << (file ? file : "Unknown");
        oss << ", line: " << line;

        // Log oss.str()
        return;
    }

    if((mode & CRYPTO_LOCK) == CRYPTO_LOCK)
    {
        int rc = pthread_mutex_lock(&s_locks[idx]);
        int err = errno;
        ASSERT(rc == 0);

        if(!(rc == 0))
        {
            ostringstream oss;
            oss << "LockingFnc: lock failed with error ";
            oss << err << ". File: " << (file ? file : "Unknown");
            oss << ", line: " << line;          

            throw runtime_error(oss.str());
        }
    }
    else if((mode & CRYPTO_UNLOCK) == CRYPTO_UNLOCK)
    {
        int rc = pthread_mutex_unlock(&s_locks[idx]);
        int err = errno;
        ASSERT(rc == 0);

        if(!(rc == 0))
        {
            ostringstream oss;
            oss << "LockingFnc: unlock failed with error ";
            oss << err << ". File: " << (file ? file : "Unknown");
            oss << ", line: " << line;

            throw runtime_error(oss.str());
        }
    }
}

unsigned long ThreadIdFnc()
{
#if defined(AC_OS_APPLE)
    ASSERT(sizeof(unsigned long) >= sizeof(pid_t));
    return static_cast<unsigned long>(pthread_mach_thread_np(pthread_self()));
#elif defined(AC_OS_STARNIX)
    ASSERT(sizeof(unsigned long) >= sizeof(pid_t));
    return static_cast<unsigned long>(gettid());
#else
# error "Unsupported platform"
#endif
}

如果您不使用 libssl,则放弃对 SSL_library_init 的调用。所有 libcrypto 都需要调用 OpenSSL_add_all_algorithms 进行初始化。


The SSL Documentation doesn't mention anything about thread-safety, though.

是的,文档有时会留下一些不足之处。我知道很多人正在通过 OpenSSL 基金会运行的 wiki 来改进它。 Matt Caswell 在 http://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography 上做了很多简单记录椭圆曲线的工作。 .他还负责 POD 文件和 MAN 页面。请记住,Matt 并未编写任何代码 - 他只是为其他人编写文档。

有初始化页面,但是没有锁的代码。它在我的 TODO 列表中。参见 http://wiki.openssl.org/index.php/Library_Initialization .

关于c - 使用 OpenSSL C 库在多线程中生成椭圆曲线 key 对 (EC_KEY_generate_key),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19955541/

相关文章:

c - C 程序的内存段

c - 在C中将小矩阵乘以大矩阵

ios - 在 iOS 7 中管理核心数据线程

C# 套接字和多线程

windows - 为 Win32 编译 OpenSSL 时出错

bash - 如何使用 GNU parallel 在巨大的数据集上并行化包含嵌套 for 循环的 bash 脚本?

c++ - GNU C 和 C++ 链接错误

c - 我如何模拟缺少文件描述符?

Python多进程/多线程加速文件复制

linux - 如何检查我的服务器 Diffie-Hellman MODP 大小(位)并增加它?