消息处理程序中的 RAND_event() 会导致 UI 中的小冻结吗?

Can RAND_event() in message handler cause little-freezes in UI?

本文关键字:UI 冻结 RAND 程序 event 消息处理      更新时间:2023-10-16

有一个消息处理程序(在C++生成器中),如下所示:

void __fastcall TMainForm::HandleMessages(tagMSG &Msg, bool &Handled)
{
    RAND_event(Msg.message, Msg.wParam, Msg.lParam);
    //...
}

RAND_event()会导致 UI 中的小冻结吗?

谢谢!

编辑:

RAND_event()来自OpenSSL,以下是它的描述:

RAND_event()从鼠标等Windows事件中收集熵 移动和其他用户交互。它应该用 发送到窗口的所有消息的 iMsg、wParam 和 lParam 参数 程序。它将估计事件消息中包含的熵 (如果有的话),并将其添加到 PRNG 中。然后,程序可以处理 消息像往常一样。

如有疑问 - 验证源代码。

cryptorandrand_win.cOpenSSL 1.0.2f

int RAND_event(UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    double add_entropy = 0;
    switch (iMsg) {
    case WM_KEYDOWN:
        {
            static WPARAM key;
            if (key != wParam)
                add_entropy = 0.05;
            key = wParam;
        }
        break;
    case WM_MOUSEMOVE:
        {
            static int lastx, lasty, lastdx, lastdy;
            int x, y, dx, dy;
            x = LOWORD(lParam);
            y = HIWORD(lParam);
            dx = lastx - x;
            dy = lasty - y;
            if (dx != 0 && dy != 0 && dx - lastdx != 0 && dy - lastdy != 0)
                add_entropy = .2;
            lastx = x, lasty = y;
            lastdx = dx, lastdy = dy;
        }
        break;
    }
    readtimer();
    RAND_add(&iMsg, sizeof(iMsg), add_entropy);
    RAND_add(&wParam, sizeof(wParam), 0);
    RAND_add(&lParam, sizeof(lParam), 0);
    return (RAND_status());
}

如您所见,这里没有循环,等待或类似的事情,只有一些基本的比较,分配和添加。您还可以自己验证RAND_event()中使用的一些功能,例如 readtimer() ,但它们也不包含任何可能导致性能显着下降的内容(至少与使用 UI 相比)。

编辑:噢,刚才看到雷米的评论了,我的错。无论如何,我只是将其保留为一个扩展答案(已经作为评论存在),从一点(很少)有点不同的观点。

RAND_event() 会导致 UI 中的小冻结吗?

或。熵是通过RAND_add添加的,这意味着熵被提取然后消化。因此,每次调用 RAND_add 时都会调用哈希函数。

如果您使用的是默认的 RAND 引擎,则使用的引擎是来自 <openssl src>cryptorandmd_rand.c 的引擎。这意味着RAND_add调用ssleay_rand_add。函数如下所示。

如果正在发送大量Windows消息,那么我可以想象桌面在消化熵时变慢的情况。在移动操作系统上,这个问题可能会变得更加严重。分析工具应该能够为您提供更多信息。


我认为可以在 Windows 上RAND_event进行优化......消息应累积,然后批量添加。也就是说,累积熵,然后在第 8 次、第 16 次或第 32 次调用时调用RAND_add

生成器的运行状况不应受到影响,因为向应用程序发送了如此多的 Windows 消息。您可以使用 Spy++ 来了解正在发送的消息数量。


如果您在某处使用RAND_poll,那么您可能会看到一个明显的块。有关此内容的详细信息,请参阅问题 #2100:由于 OpenSSL 错误跟踪器上的 Heap32Next,RAND_poll Windows 7 上的速度可能会非常慢。


static void ssleay_rand_add(const void *buf, int num, double add)
{
    int i, j, k, st_idx;
    long md_c[2];
    unsigned char local_md[MD_DIGEST_LENGTH];
    EVP_MD_CTX m;
    int do_not_lock;
    if (!num)
        return;
    /*
     * (Based on the rand(3) manpage)
     *
     * The input is chopped up into units of 20 bytes (or less for
     * the last block).  Each of these blocks is run through the hash
     * function as follows:  The data passed to the hash function
     * is the current 'md', the same number of bytes from the 'state'
     * (the location determined by in incremented looping index) as
     * the current 'block', the new key data 'block', and 'count'
     * (which is incremented after each use).
     * The result of this is kept in 'md' and also xored into the
     * 'state' at the same locations that were used as input into the
     * hash function.
     */
    /* check if we already have the lock */
    if (crypto_lock_rand) {
        CRYPTO_THREADID cur;
        CRYPTO_THREADID_current(&cur);
        CRYPTO_r_lock(CRYPTO_LOCK_RAND2);
        do_not_lock = !CRYPTO_THREADID_cmp(&locking_threadid, &cur);
        CRYPTO_r_unlock(CRYPTO_LOCK_RAND2);
    } else
        do_not_lock = 0;
    if (!do_not_lock)
        CRYPTO_w_lock(CRYPTO_LOCK_RAND);
    st_idx = state_index;
    /*
     * use our own copies of the counters so that even if a concurrent thread
     * seeds with exactly the same data and uses the same subarray there's
     * _some_ difference
     */
    md_c[0] = md_count[0];
    md_c[1] = md_count[1];
    memcpy(local_md, md, sizeof md);
    /* state_index <= state_num <= STATE_SIZE */
    state_index += num;
    if (state_index >= STATE_SIZE) {
        state_index %= STATE_SIZE;
        state_num = STATE_SIZE;
    } else if (state_num < STATE_SIZE) {
        if (state_index > state_num)
            state_num = state_index;
    }
    /* state_index <= state_num <= STATE_SIZE */
    /*
     * state[st_idx], ..., state[(st_idx + num - 1) % STATE_SIZE] are what we
     * will use now, but other threads may use them as well
     */
    md_count[1] += (num / MD_DIGEST_LENGTH) + (num % MD_DIGEST_LENGTH > 0);
    if (!do_not_lock)
        CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
    EVP_MD_CTX_init(&m);
    for (i = 0; i < num; i += MD_DIGEST_LENGTH) {
        j = (num - i);
        j = (j > MD_DIGEST_LENGTH) ? MD_DIGEST_LENGTH : j;
        MD_Init(&m);
        MD_Update(&m, local_md, MD_DIGEST_LENGTH);
        k = (st_idx + j) - STATE_SIZE;
        if (k > 0) {
            MD_Update(&m, &(state[st_idx]), j - k);
            MD_Update(&m, &(state[0]), k);
        } else
            MD_Update(&m, &(state[st_idx]), j);
        /* DO NOT REMOVE THE FOLLOWING CALL TO MD_Update()! */
        MD_Update(&m, buf, j);
        /*
         * We know that line may cause programs such as purify and valgrind
         * to complain about use of uninitialized data.  The problem is not,
         * it's with the caller.  Removing that line will make sure you get
         * really bad randomness and thereby other problems such as very
         * insecure keys.
         */
        MD_Update(&m, (unsigned char *)&(md_c[0]), sizeof(md_c));
        MD_Final(&m, local_md);
        md_c[1]++;
        buf = (const char *)buf + j;
        for (k = 0; k < j; k++) {
            /*
             * Parallel threads may interfere with this, but always each byte
             * of the new state is the XOR of some previous value of its and
             * local_md (itermediate values may be lost). Alway using locking
             * could hurt performance more than necessary given that
             * conflicts occur only when the total seeding is longer than the
             * random state.
             */
            state[st_idx++] ^= local_md[k];
            if (st_idx >= STATE_SIZE)
                st_idx = 0;
        }
    }
    EVP_MD_CTX_cleanup(&m);
    if (!do_not_lock)
        CRYPTO_w_lock(CRYPTO_LOCK_RAND);
    /*
     * Don't just copy back local_md into md -- this could mean that other
     * thread's seeding remains without effect (except for the incremented
     * counter).  By XORing it we keep at least as much entropy as fits into
     * md.
     */
    for (k = 0; k < (int)sizeof(md); k++) {
        md[k] ^= local_md[k];
    }
    if (entropy < ENTROPY_NEEDED) /* stop counting when we have enough */
        entropy += add;
    if (!do_not_lock)
        CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
}