3

多くの ec キーペアを生成したい。プロセスを少し高速化するために、このジョブに複数のスレッドを使用するようにアプリケーションを書き直しました。以下は、各スレッドがキーを生成する方法のコード スニペットです。

(...)
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 で 4 つのスレッドを使用すると、アプリケーションは 2 倍の速さで実行されました。しかし、その後 3 ~ 4 回の E6 キー生成の後、突然セグメンテーション違反が発生します。EC_KEY_generate_key 操作をロックすると、セグメンテーション違反はなくなりますが、複数のスレッドを使用する利点はなくなります。今私の質問。メモリを破損することなく、キーの作成を複数のスレッドに分割することは可能ですか? グーグルを使っても情報が見つかりませんでした。ただし、SSL ドキュメントでは、スレッド セーフについては何も言及されていません。どんな助けでも大歓迎です。どうも

4

1 に答える 1

5
// 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.c125 行あたりからです。乱数発生器と楕円曲線の歯車がリストを構成していることに注意してください。

/* 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を参照してください。

于 2013-12-01T23:29:45.347 に答える