2015-04-11 12:59:22 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include <library.h>
|
|
|
|
|
|
|
|
typedef bool (*attackfn_t)(void *subj, u_char *data, size_t len);
|
|
|
|
|
|
|
|
static void start_timing(struct timespec *start)
|
|
|
|
{
|
|
|
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, start);
|
|
|
|
}
|
|
|
|
|
2016-03-22 12:22:01 +00:00
|
|
|
static uint64_t end_timing(struct timespec *start)
|
2015-04-11 12:59:22 +00:00
|
|
|
{
|
|
|
|
struct timespec end;
|
|
|
|
|
|
|
|
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
|
|
|
|
return (end.tv_nsec - start->tv_nsec) +
|
|
|
|
(end.tv_sec - start->tv_sec) * 1000000000;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int intcmp(const void *a, const void *b)
|
|
|
|
{
|
2016-03-22 12:22:01 +00:00
|
|
|
return *(uint64_t*)a - *(uint64_t*)b;
|
2015-04-11 12:59:22 +00:00
|
|
|
}
|
|
|
|
|
2016-03-22 12:22:01 +00:00
|
|
|
static uint64_t median(uint64_t *m, int count)
|
2015-04-11 12:59:22 +00:00
|
|
|
{
|
2016-03-22 12:22:01 +00:00
|
|
|
qsort(m, count, sizeof(uint64_t), intcmp);
|
2015-04-11 12:59:22 +00:00
|
|
|
return m[count / 2];
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool timeattack(attackfn_t attackfn, void *subj, size_t dlen,
|
|
|
|
u_int iterations, u_int distance)
|
|
|
|
{
|
|
|
|
struct timespec start;
|
|
|
|
u_char test[dlen];
|
2016-03-22 12:22:01 +00:00
|
|
|
uint64_t mini, maxi, t[256], m[256][10];
|
2015-04-11 12:59:22 +00:00
|
|
|
float fastdist = 0, slowdist = 0;
|
|
|
|
int i, j, k, l, byte, limit, retry = 0;
|
|
|
|
int fastest = 0, slowest = 0;
|
|
|
|
|
|
|
|
memset(test, 0, dlen);
|
|
|
|
|
|
|
|
/* do some iterations to fill caches */
|
|
|
|
for (i = 0; i < iterations; i++)
|
|
|
|
{
|
|
|
|
attackfn(subj, test, dlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (byte = 0; byte < dlen;)
|
|
|
|
{
|
|
|
|
memset(t, 0, sizeof(t));
|
|
|
|
memset(m, 0, sizeof(m));
|
|
|
|
|
|
|
|
limit = iterations * (retry + 1);
|
|
|
|
|
|
|
|
/* measure timing for all patterns in next byte */
|
|
|
|
for (k = 0; k < 10; k++)
|
|
|
|
{
|
|
|
|
for (j = 0; j < 256; j++)
|
|
|
|
{
|
|
|
|
for (l = 0; l < 100; l++)
|
|
|
|
{
|
|
|
|
test[byte] = j;
|
|
|
|
start_timing(&start);
|
|
|
|
for (i = 0; i < limit; i++)
|
|
|
|
{
|
|
|
|
attackfn(subj, test, dlen);
|
|
|
|
}
|
|
|
|
m[j][k] += end_timing(&start);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < 256; j++)
|
|
|
|
{
|
|
|
|
t[j] = median(m[j], countof(m[j]));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find fastest/slowest runs */
|
|
|
|
mini = ~0;
|
|
|
|
maxi = 0;
|
|
|
|
for (j = 0; j < 256; j++)
|
|
|
|
{
|
|
|
|
if (t[j] < mini)
|
|
|
|
{
|
|
|
|
mini = min(t[j], mini);
|
|
|
|
fastest = j;
|
|
|
|
}
|
|
|
|
if (t[j] > maxi)
|
|
|
|
{
|
|
|
|
maxi = max(t[j], maxi);
|
|
|
|
slowest = j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* calculate distance to next result */
|
|
|
|
mini = ~0;
|
|
|
|
maxi = 0;
|
|
|
|
for (j = 0; j < 256; j++)
|
|
|
|
{
|
|
|
|
if (fastest != j && t[j] < mini)
|
|
|
|
{
|
|
|
|
mini = min(t[j], mini);
|
|
|
|
fastdist = (float)(t[j] - t[fastest]) / distance;
|
|
|
|
}
|
|
|
|
if (slowest != j && t[j] > maxi)
|
|
|
|
{
|
|
|
|
maxi = max(t[j], maxi);
|
|
|
|
slowdist = (float)(t[slowest] - t[j]) / distance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fastdist > 1.0f)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "byte %02d: %02x (fastest, dist %02.2f)\n",
|
|
|
|
byte, fastest, fastdist);
|
|
|
|
test[byte] = fastest;
|
|
|
|
retry = 0;
|
|
|
|
byte++;
|
|
|
|
}
|
|
|
|
else if (slowdist > 1.0f)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "byte %02d: %02x (slowest, dist %02.2f)\n",
|
|
|
|
byte, slowest, slowdist);
|
|
|
|
test[byte] = slowest;
|
|
|
|
retry = 0;
|
|
|
|
byte++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (retry++ > 5 && byte > 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "distance fastest %02.2f (%02x), "
|
|
|
|
"slowest %02.2f (%02x), stepping back\n",
|
|
|
|
fastdist, fastest, slowdist, slowest);
|
|
|
|
test[byte--] = 0;
|
|
|
|
}
|
|
|
|
else if (retry < 10)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "distance fastest %02.2f (%02x), "
|
|
|
|
"slowest %02.2f (%02x), retrying (%d)\n",
|
|
|
|
fastdist, fastest, slowdist, slowest, retry);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf("attack failed, giving up\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (attackfn(subj, test, dlen))
|
|
|
|
{
|
|
|
|
printf("attack successful with %b\n", test, dlen);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
printf("attack failed with %b\n", test, dlen);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
CALLBACK(attack_memeq1, bool,
|
|
|
|
u_char *subj, u_char *data, size_t len)
|
|
|
|
{
|
|
|
|
return memeq(data, subj, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
CALLBACK(attack_memeq2, bool,
|
|
|
|
u_char *subj, u_char *data, size_t len)
|
|
|
|
{
|
|
|
|
return memeq(subj, data, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
CALLBACK(attack_memeq3, bool,
|
|
|
|
u_char *subj, u_char *data, size_t len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
{
|
|
|
|
if (subj[i] != data[i])
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
CALLBACK(attack_memeq4, bool,
|
|
|
|
u_char *subj, u_char *data, size_t len)
|
|
|
|
{
|
|
|
|
int i, m = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
{
|
|
|
|
m |= subj[i] != data[i];
|
|
|
|
}
|
|
|
|
return !m;
|
|
|
|
}
|
|
|
|
|
2015-04-11 14:44:18 +00:00
|
|
|
CALLBACK(attack_memeq5, bool,
|
|
|
|
u_char *subj, u_char *data, size_t len)
|
|
|
|
{
|
|
|
|
return memeq_const(subj, data, len);
|
|
|
|
}
|
|
|
|
|
2015-04-11 12:59:22 +00:00
|
|
|
static bool attack_memeq(char *name, u_int iterations, u_int distance)
|
|
|
|
{
|
|
|
|
struct {
|
|
|
|
char *name;
|
|
|
|
attackfn_t fn;
|
|
|
|
} attacks[] = {
|
|
|
|
{ "memeq1", attack_memeq1 },
|
|
|
|
{ "memeq2", attack_memeq2 },
|
|
|
|
{ "memeq3", attack_memeq3 },
|
|
|
|
{ "memeq4", attack_memeq4 },
|
2015-04-11 14:44:18 +00:00
|
|
|
{ "memeq5", attack_memeq5 },
|
2015-04-11 12:59:22 +00:00
|
|
|
};
|
|
|
|
u_char exp[16];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
srandom(time(NULL));
|
|
|
|
for (i = 0; i < sizeof(exp); i++)
|
|
|
|
{
|
|
|
|
exp[i] = random();
|
|
|
|
}
|
|
|
|
fprintf(stderr, "attacking %b\n", exp, sizeof(exp));
|
|
|
|
|
|
|
|
for (i = 0; i < countof(attacks); i++)
|
|
|
|
{
|
|
|
|
if (streq(name, attacks[i].name))
|
|
|
|
{
|
|
|
|
return timeattack(attacks[i].fn, exp, sizeof(exp),
|
|
|
|
iterations, distance);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2015-04-11 13:55:26 +00:00
|
|
|
CALLBACK(attack_chunk1, bool,
|
|
|
|
u_char *subj, u_char *data, size_t len)
|
|
|
|
{
|
|
|
|
return chunk_equals(chunk_create(subj, len), chunk_create(data, len));
|
|
|
|
}
|
|
|
|
|
|
|
|
CALLBACK(attack_chunk2, bool,
|
|
|
|
u_char *subj, u_char *data, size_t len)
|
|
|
|
{
|
|
|
|
return chunk_equals_const(chunk_create(subj, len), chunk_create(data, len));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool attack_chunk(char *name, u_int iterations, u_int distance)
|
|
|
|
{
|
|
|
|
struct {
|
|
|
|
char *name;
|
|
|
|
attackfn_t fn;
|
|
|
|
} attacks[] = {
|
|
|
|
{ "chunk1", attack_chunk1 },
|
|
|
|
{ "chunk2", attack_chunk2 },
|
|
|
|
};
|
|
|
|
u_char exp[16];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
srandom(time(NULL));
|
|
|
|
for (i = 0; i < sizeof(exp); i++)
|
|
|
|
{
|
|
|
|
exp[i] = random();
|
|
|
|
}
|
|
|
|
fprintf(stderr, "attacking %b\n", exp, sizeof(exp));
|
|
|
|
|
|
|
|
for (i = 0; i < countof(attacks); i++)
|
|
|
|
{
|
|
|
|
if (streq(name, attacks[i].name))
|
|
|
|
{
|
|
|
|
return timeattack(attacks[i].fn, exp, sizeof(exp),
|
|
|
|
iterations, distance);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2015-04-11 12:59:22 +00:00
|
|
|
CALLBACK(attack_aead, bool,
|
|
|
|
aead_t *aead, u_char *data, size_t len)
|
|
|
|
{
|
|
|
|
u_char iv[aead->get_iv_size(aead)];
|
|
|
|
|
|
|
|
memset(iv, 0, sizeof(iv));
|
|
|
|
return aead->decrypt(aead, chunk_create(data, len), chunk_empty,
|
|
|
|
chunk_from_thing(iv), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool attack_aeads(encryption_algorithm_t alg, size_t key_size,
|
|
|
|
u_int iterations, u_int distance)
|
|
|
|
{
|
|
|
|
u_char buf[64];
|
|
|
|
aead_t *aead;
|
|
|
|
bool res;
|
|
|
|
|
|
|
|
aead = lib->crypto->create_aead(lib->crypto, alg, key_size, 0);
|
|
|
|
if (!aead)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "creating AEAD %N failed\n",
|
|
|
|
encryption_algorithm_names, alg);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
memset(buf, 0xe3, sizeof(buf));
|
|
|
|
if (!aead->set_key(aead, chunk_create(buf, aead->get_key_size(aead))))
|
|
|
|
{
|
|
|
|
aead->destroy(aead);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
memset(buf, 0, aead->get_iv_size(aead));
|
|
|
|
if (!aead->encrypt(aead, chunk_create(buf, 0), chunk_empty,
|
|
|
|
chunk_create(buf, aead->get_iv_size(aead)), NULL))
|
|
|
|
{
|
|
|
|
aead->destroy(aead);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
fprintf(stderr, "attacking %b\n", buf, aead->get_icv_size(aead));
|
|
|
|
|
|
|
|
res = timeattack(attack_aead, aead, aead->get_icv_size(aead),
|
|
|
|
iterations, distance);
|
|
|
|
aead->destroy(aead);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
CALLBACK(attack_signer, bool,
|
|
|
|
signer_t *signer, u_char *data, size_t len)
|
|
|
|
{
|
|
|
|
return signer->verify_signature(signer, chunk_empty, chunk_create(data, len));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool attack_signers(integrity_algorithm_t alg,
|
|
|
|
u_int iterations, u_int distance)
|
|
|
|
{
|
|
|
|
u_char buf[64];
|
|
|
|
signer_t *signer;
|
|
|
|
bool res;
|
|
|
|
|
|
|
|
signer = lib->crypto->create_signer(lib->crypto, alg);
|
|
|
|
if (!signer)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "creating signer %N failed\n",
|
|
|
|
integrity_algorithm_names, alg);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
memset(buf, 0xe3, sizeof(buf));
|
|
|
|
if (!signer->set_key(signer, chunk_create(buf, signer->get_key_size(signer))))
|
|
|
|
{
|
|
|
|
signer->destroy(signer);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (!signer->get_signature(signer, chunk_empty, buf))
|
|
|
|
{
|
|
|
|
signer->destroy(signer);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
fprintf(stderr, "attacking %b\n", buf, signer->get_block_size(signer));
|
|
|
|
|
|
|
|
res = timeattack(attack_signer, signer, signer->get_block_size(signer),
|
|
|
|
iterations, distance);
|
|
|
|
signer->destroy(signer);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool attack_transform(char *name, u_int iterations, u_int distance)
|
|
|
|
{
|
|
|
|
const proposal_token_t *token;
|
|
|
|
|
|
|
|
token = lib->proposal->get_token(lib->proposal, name);
|
|
|
|
if (!token)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "algorithm '%s' unknown\n", name);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (token->type)
|
|
|
|
{
|
|
|
|
case ENCRYPTION_ALGORITHM:
|
|
|
|
if (encryption_algorithm_is_aead(token->algorithm))
|
|
|
|
{
|
|
|
|
return attack_aeads(token->algorithm, token->keysize / 8,
|
|
|
|
iterations, distance);
|
|
|
|
}
|
|
|
|
fprintf(stderr, "can't attack a crypter\n");
|
|
|
|
return FALSE;
|
|
|
|
case INTEGRITY_ALGORITHM:
|
|
|
|
return attack_signers(token->algorithm, iterations, distance);
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "can't attack a %N\n", transform_type_names, token->type);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
library_init(NULL, "timeattack");
|
|
|
|
atexit(library_deinit);
|
|
|
|
lib->plugins->load(lib->plugins, getenv("PLUGINS") ?: PLUGINS);
|
|
|
|
|
|
|
|
if (argc < 3)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "usage: %s <attack> <iterations> <distance>\n", argv[0]);
|
2015-04-11 13:55:26 +00:00
|
|
|
fprintf(stderr, " <attack>: memeq[1-5] / chunk[1-2] / aead / signer\n");
|
2015-04-11 12:59:22 +00:00
|
|
|
fprintf(stderr, " <iterations>: number of invocations * 1000\n");
|
|
|
|
fprintf(stderr, " <distance>: time difference in ns for a hit\n");
|
|
|
|
fprintf(stderr, " example: %s memeq1 100 500\n", argv[0]);
|
|
|
|
fprintf(stderr, " example: %s aes128gcm16 100 4000\n", argv[0]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (strpfx(argv[1], "memeq"))
|
|
|
|
{
|
|
|
|
return !attack_memeq(argv[1], atoi(argv[2]), atoi(argv[3]));
|
|
|
|
}
|
2015-04-11 13:55:26 +00:00
|
|
|
if (strpfx(argv[1], "chunk"))
|
|
|
|
{
|
|
|
|
return !attack_chunk(argv[1], atoi(argv[2]), atoi(argv[3]));
|
|
|
|
}
|
2015-04-11 12:59:22 +00:00
|
|
|
return !attack_transform(argv[1], atoi(argv[2]), atoi(argv[3]));
|
|
|
|
}
|