の素朴な実装は次のmemcmp()
ようになります(この回答から):
int memcmp_test(const char *cs_in, const char *ct_in, size_t n)
{
size_t i;
const unsigned char * cs = (const unsigned char*) cs_in;
const unsigned char * ct = (const unsigned char*) ct_in;
for (i = 0; i < n; i++, cs++, ct++)
{
if (*cs < *ct)
{
return -1;
}
else if (*cs > *ct)
{
return 1;
}
}
return 0;
}
ここでは、最初の不一致バイトが見つかると、ブロックのトラバーサルが停止します。これは、実行時間がブロックの内容に依存するようになり、タイミング攻撃が可能になるため、暗号化アプリケーションには適していません。したがって、OpenSSL はこれを使用します (ここから取得):
int CRYPTO_memcmp(const void *in_a, const void *in_b, size_t len)
{
size_t i;
const unsigned char *a = in_a;
const unsigned char *b = in_b;
unsigned char x = 0;
for (i = 0; i < len; i++)
x |= a[i] ^ b[i];
return x;
}
break
途中にs やsがないreturn
ため、このコードはブロック全体をトラバースする必要があります。少なくともこれは意図です。
次に、使用例を 1 つ示します (ここから):
static int des_ede3_unwrap(EVP_CIPHER_CTX *ctx,
unsigned char *out, const unsigned char *in, size_t inl)
{
unsigned char icv[8], iv[8], sha1tmp[SHA_DIGEST_LENGTH];
//whatever, unrelated then...
if (!CRYPTO_memcmp(sha1tmp, icv, 8))
rv = inl - 16;
//whatever, unrelated
}
リンク時のコード生成 (Visual C++ LTCG) またはリンク時の最適化 (gcc LTO) を使用すると、コンパイラはCRYPTO_memcmp()
実装と呼び出しサイトの両方を確認できます (それらが異なる翻訳単位にある場合でも)。呼び出しサイトは実際の値を使用せず、null と比較するだけであることがわかります。したがってCRYPTO_memcmp()
、最初の不一致のバイト ペアを見つけたらすぐに戻るように自由に変換でき、「安全な」バージョンのmemcmp()
は安全ではなくなります。
memcmp()
標準準拠のコンパイラがタイミング攻撃を支援するバージョンに変換しないように実装するにはどうすればよいですか?