diff options
Diffstat (limited to 'scripts/timeattack.c')
| -rw-r--r-- | scripts/timeattack.c | 418 | 
1 files changed, 418 insertions, 0 deletions
diff --git a/scripts/timeattack.c b/scripts/timeattack.c new file mode 100644 index 000000000..ef00e8c4e --- /dev/null +++ b/scripts/timeattack.c @@ -0,0 +1,418 @@ +#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); +} + +static u_int64_t end_timing(struct timespec *start) +{ +	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) +{ +	return *(u_int64_t*)a - *(u_int64_t*)b; +} + +static u_int64_t median(u_int64_t *m, int count) +{ +	qsort(m, count, sizeof(u_int64_t), intcmp); +	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]; +	u_int64_t mini, maxi, t[256], m[256][10]; +	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; +} + +CALLBACK(attack_memeq5, bool, +	u_char *subj, u_char *data, size_t len) +{ +	return memeq_const(subj, data, len); +} + +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 }, +		{ "memeq5", attack_memeq5 }, +	}; +	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; +} + +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; +} + +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]); +		fprintf(stderr, "  <attack>: memeq[1-5] / chunk[1-2] / aead / signer\n"); +		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])); +	} +	if (strpfx(argv[1], "chunk")) +	{ +		return !attack_chunk(argv[1], atoi(argv[2]), atoi(argv[3])); +	} +	return !attack_transform(argv[1], atoi(argv[2]), atoi(argv[3])); +}  | 
