消息处理程序中的 RAND_event() 会导致 UI 中的小冻结吗?
Can RAND_event() in message handler cause little-freezes in UI?
有一个这样的消息处理程序(在 C++ Builder 中):
void __fastcall TMainForm::HandleMessages(tagMSG &Msg, bool &Handled)
{
RAND_event(Msg.message, Msg.wParam, Msg.lParam);
//...
}
RAND_event()
会导致 UI 中的小冻结吗?
谢谢!
编辑:
RAND_event()
来自 OpenSSL,这里是它的描述:
RAND_event()
collects the entropy from Windows events such as mouse
movements and other user interaction. It should be called with the
iMsg, wParam and lParam arguments of all messages sent to the window
procedure. It will estimate the entropy contained in the event message
(if any), and add it to the PRNG. The program can then process the
messages as usual.
如有疑问 - 请验证源代码。
来自 \crypto\rand\rand_win.c
(OpenSSL 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()
,但它们也不包含任何可能导致性能显着下降的内容(至少与使用 [=24 相比时是这样的) =]).
编辑:
噢,我刚才看到雷米的评论,我的错。无论如何,我从一个(非常小的)有点不同的观点来看,将其作为扩展答案(已经作为评论存在)保留。
Can RAND_event() cause little-freezes in UI?
也许吧。通过RAND_add
添加熵,这意味着熵被提取然后被消化。因此,每次调用 RAND_add
.
时都会调用哈希函数
如果您使用默认的 RAND 引擎,则使用的引擎是 <openssl src>\crypto\rand\md_rand.c
中的引擎。这意味着 RAND_add
调用了 ssleay_rand_add
。函数如下图
如果正在发送大量 Windows 消息,那么我可以设想桌面在消化熵时变慢的情况。这个问题在移动操作系统上可能会变得更加严重。分析工具应该能够为您提供更多信息。
我认为 Windows 上的 RAND_event
可以进行优化...消息应累积,然后批量添加。即累积熵,然后在第 8 次或第 16 次或第 32 次调用时调用 RAND_add
。
生成器的运行状况不应受到影响,因为有如此多的 Windows 消息发送到应用程序。您可以使用 Spy++ 来了解发送的消息数。
如果您在某处使用 RAND_poll
,那么您会看到一个明显的块。有关详细信息,请参阅 OpenSSL 错误跟踪器上的 Issue #2100: RAND_poll can be incredibly slow on Windows7 due to Heap32Next。
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);
}
有一个这样的消息处理程序(在 C++ Builder 中):
void __fastcall TMainForm::HandleMessages(tagMSG &Msg, bool &Handled)
{
RAND_event(Msg.message, Msg.wParam, Msg.lParam);
//...
}
RAND_event()
会导致 UI 中的小冻结吗?
谢谢!
编辑:
RAND_event()
来自 OpenSSL,这里是它的描述:
RAND_event()
collects the entropy from Windows events such as mouse movements and other user interaction. It should be called with the iMsg, wParam and lParam arguments of all messages sent to the window procedure. It will estimate the entropy contained in the event message (if any), and add it to the PRNG. The program can then process the messages as usual.
如有疑问 - 请验证源代码。
来自 \crypto\rand\rand_win.c
(OpenSSL 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()
,但它们也不包含任何可能导致性能显着下降的内容(至少与使用 [=24 相比时是这样的) =]).
编辑: 噢,我刚才看到雷米的评论,我的错。无论如何,我从一个(非常小的)有点不同的观点来看,将其作为扩展答案(已经作为评论存在)保留。
Can RAND_event() cause little-freezes in UI?
也许吧。通过RAND_add
添加熵,这意味着熵被提取然后被消化。因此,每次调用 RAND_add
.
如果您使用默认的 RAND 引擎,则使用的引擎是 <openssl src>\crypto\rand\md_rand.c
中的引擎。这意味着 RAND_add
调用了 ssleay_rand_add
。函数如下图
如果正在发送大量 Windows 消息,那么我可以设想桌面在消化熵时变慢的情况。这个问题在移动操作系统上可能会变得更加严重。分析工具应该能够为您提供更多信息。
我认为 Windows 上的 RAND_event
可以进行优化...消息应累积,然后批量添加。即累积熵,然后在第 8 次或第 16 次或第 32 次调用时调用 RAND_add
。
生成器的运行状况不应受到影响,因为有如此多的 Windows 消息发送到应用程序。您可以使用 Spy++ 来了解发送的消息数。
如果您在某处使用 RAND_poll
,那么您会看到一个明显的块。有关详细信息,请参阅 OpenSSL 错误跟踪器上的 Issue #2100: RAND_poll can be incredibly slow on Windows7 due to Heap32Next。
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);
}