// 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 のスレッド (3)を参照してください。
メモリを破損することなく、キーの作成を複数のスレッドに分割することは可能ですか?
はい。ただし、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
初期化するための呼び出しです。
ただし、SSL ドキュメントには、スレッドセーフについては何も言及されていません。
ええ、ドキュメントは時々望まれるものを残します。OpenSSL Foundation が運営する wiki を通じて、多くの人々が改善に取り組んでいることを私は知っています。Matt Caswell は、http://wiki.openssl.org/index.php/Elliptic_Curve_Cryptographyで楕円曲線の内容を簡単に文書化するために多くの作業を行いました。また、POD ファイルと MAN ページも担当しています。Matt はコードを書いていないことに注意してください。彼は他の人のためにドキュメントを作成しているだけです。
初期化に関するページがありますが、ロックのコードはありません。それは私のTODOリストにあります。http://wiki.openssl.org/index.php/Library_Initializationを参照してください。