RSA_generate_key
進行状況のコールバックを提供するので、それからlongjmp
関数を終了することができます。少し追加のコードをRSA_generate_key
使用して、ウィンドウシステムによって設定されたタイムアウトまたはフラグをチェックするために使用できる汎用テスト関数を受け入れるラッパーを作成できます。
#include <openssl/rsa.h>
#include <stdbool.h>
#include <setjmp.h>
struct trampoline_ctx {
bool (*testfn)(void *);
void *testfn_arg;
jmp_buf env;
};
static void trampoline(int ignore1, int ignore2, void *arg)
{
struct trampoline_ctx *ctx = arg;
if (!ctx->testfn(ctx->testfn_arg))
longjmp(ctx->env, 1);
}
// like RSA_generate_key, but accepts a test function. If testfn returns
// false, key generation is terminated and NULL is returned.
RSA *
my_generate_key(int num, unsigned long e,
bool (*testfn)(void *), void *testfn_arg)
{
struct trampoline_ctx ctx;
ctx.testfn = testfn;
ctx.testfn_arg = testfn_arg;
if (setjmp(ctx.env))
return NULL;
return RSA_generate_key(num, e, trampoline, &ctx);
}
このアプローチはlongjmp
、C89とC99の両方で義務付けられているように、驚くほど移植性があります。その欠点は、長い間使用している関数がリソースを動的に割り当てると、リソースがリークする可能性があることです。ただし、実際には、リークは、まれにしか実行されない場合、または明示的なユーザーリクエストでのみ実行される場合、気付かないほど小さい場合があります。これが事実であることを確認するには、コードをタイトループで実行し、プロセスのリソース消費を観察します。
上記の関数のテストプログラムは次のとおりです。
#include <stdio.h>
#include <sys/time.h>
double now()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + (double) tv.tv_usec / 1e6;
}
struct tt_ctx {
double start;
double limit;
};
bool test_time_limit(void *arg)
{
struct tt_ctx *ctx = arg;
return now() - ctx->start <= ctx->limit;
}
int main(int argc, char **argv)
{
int limit = atoi(argv[1]);
struct tt_ctx ctx;
ctx.start = now();
ctx.limit = limit / 1000.0;
RSA *key = my_generate_key(4096, 65537, test_time_limit, &ctx);
printf("%p\n", key);
return 0;
}
テストプログラムはPOSIXgettimeofday
を想定していますが、システムが提供する別の高解像度クロックに簡単に変換できます。テスト手順は次のとおりです。
両方のコードをファイルに追加し、でコンパイルし-lrsa
ます。テストプログラムは、コマンドラインでミリ秒単位で指定された制限時間内に4096ビットのRSAキーを生成します。すべての場合において、結果のRSA *
ポインターを出力してmy_generate_key
、要求が完了したか中止されたかを示します。time
制限時間が守られていることを確認するための健全性チェックとしての実行に伴う出力:
# try with a 10ms limit
$ time ./a.out 10
(nil) # too short
./a.out 10 0.02s user 0.00s system 85% cpu 0.023 total
# see if 100ms is enough time
$ time ./a.out 100
(nil) # still too short
./a.out 100 0.10s user 0.00s system 97% cpu 0.106 total
# try with 1 whole second:
$ time ./a.out 1000
0x2369010 # success!
./a.out 1000 0.64s user 0.00s system 99% cpu 0.649 total