mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 13:53:31 +01:00
Split the slow unit tests into their own binary
This can run in parallel with the faster ones and the other tests.
This commit is contained in:
parent
420037dcef
commit
7322de15dc
2
.gitignore
vendored
2
.gitignore
vendored
@ -160,11 +160,13 @@ cscope.*
|
||||
/src/test/bench
|
||||
/src/test/bench.exe
|
||||
/src/test/test
|
||||
/src/test/test-slow
|
||||
/src/test/test-bt-cl
|
||||
/src/test/test-child
|
||||
/src/test/test-ntor-cl
|
||||
/src/test/test_workqueue
|
||||
/src/test/test.exe
|
||||
/src/test/test-slow.exe
|
||||
/src/test/test-bt-cl.exe
|
||||
/src/test/test-child.exe
|
||||
/src/test/test-ntor-cl.exe
|
||||
|
3
changes/ticket13243
Normal file
3
changes/ticket13243
Normal file
@ -0,0 +1,3 @@
|
||||
o Testing:
|
||||
- Move the slower unit tests into a new "./src/test/test-slow" binary
|
||||
that can be run independently of the other tests. Closes ticket 13243.
|
@ -1,8 +1,12 @@
|
||||
TESTS += src/test/test
|
||||
TESTS += src/test/test src/test/test-slow
|
||||
|
||||
noinst_PROGRAMS+= src/test/bench
|
||||
if UNITTESTS_ENABLED
|
||||
noinst_PROGRAMS+= src/test/test src/test/test-child src/test/test_workqueue
|
||||
noinst_PROGRAMS+= \
|
||||
src/test/test \
|
||||
src/test/test-slow \
|
||||
src/test/test-child \
|
||||
src/test/test_workqueue
|
||||
endif
|
||||
|
||||
src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
|
||||
@ -57,8 +61,17 @@ src_test_test_SOURCES = \
|
||||
src/test/test_status.c \
|
||||
src/test/test_threads.c \
|
||||
src/test/test_util.c \
|
||||
src/test/testing_common.c \
|
||||
src/ext/tinytest.c
|
||||
|
||||
src_test_test_slow_SOURCES = \
|
||||
src/test/test_slow.c \
|
||||
src/test/test_crypto_slow.c \
|
||||
src/test/test_util_slow.c \
|
||||
src/test/testing_common.c \
|
||||
src/ext/tinytest.c
|
||||
|
||||
|
||||
src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
|
||||
|
||||
src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS)
|
||||
@ -80,6 +93,11 @@ src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
|
||||
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
|
||||
@TOR_SYSTEMD_LIBS@
|
||||
|
||||
src_test_test_slow_CPPFLAGS = $(src_test_test_CPPFLAGS)
|
||||
src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS)
|
||||
src_test_test_slow_LDADD = $(src_test_test_LDADD)
|
||||
src_test_test_slow_LDFLAGS = $(src_test_test_LDFLAGS)
|
||||
|
||||
src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
|
||||
@TOR_LDFLAGS_libevent@
|
||||
src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \
|
||||
|
271
src/test/test.c
271
src/test/test.c
@ -3,10 +3,6 @@
|
||||
* Copyright (c) 2007-2015, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/* Ordinarily defined in tor_main.c; this bit is just here to provide one
|
||||
* since we're not linking to tor_main.c */
|
||||
const char tor_git_revision[] = "";
|
||||
|
||||
/**
|
||||
* \file test.c
|
||||
* \brief Unit tests for many pieces of the lower level Tor modules.
|
||||
@ -67,171 +63,6 @@ double fabs(double x);
|
||||
#include "crypto_curve25519.h"
|
||||
#include "onion_ntor.h"
|
||||
|
||||
#ifdef USE_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include "main.h"
|
||||
#endif
|
||||
|
||||
/** Set to true if any unit test has failed. Mostly, this is set by the macros
|
||||
* in test.h */
|
||||
int have_failed = 0;
|
||||
|
||||
/** Temporary directory (set up by setup_directory) under which we store all
|
||||
* our files during testing. */
|
||||
static char temp_dir[256];
|
||||
#ifdef _WIN32
|
||||
#define pid_t int
|
||||
#endif
|
||||
static pid_t temp_dir_setup_in_pid = 0;
|
||||
|
||||
/** Select and create the temporary directory we'll use to run our unit tests.
|
||||
* Store it in <b>temp_dir</b>. Exit immediately if we can't create it.
|
||||
* idempotent. */
|
||||
static void
|
||||
setup_directory(void)
|
||||
{
|
||||
static int is_setup = 0;
|
||||
int r;
|
||||
char rnd[256], rnd32[256];
|
||||
if (is_setup) return;
|
||||
|
||||
/* Due to base32 limitation needs to be a multiple of 5. */
|
||||
#define RAND_PATH_BYTES 5
|
||||
crypto_rand(rnd, RAND_PATH_BYTES);
|
||||
base32_encode(rnd32, sizeof(rnd32), rnd, RAND_PATH_BYTES);
|
||||
|
||||
#ifdef _WIN32
|
||||
{
|
||||
char buf[MAX_PATH];
|
||||
const char *tmp = buf;
|
||||
const char *extra_backslash = "";
|
||||
/* If this fails, we're probably screwed anyway */
|
||||
if (!GetTempPathA(sizeof(buf),buf))
|
||||
tmp = "c:\\windows\\temp\\";
|
||||
if (strcmpend(tmp, "\\")) {
|
||||
/* According to MSDN, it should be impossible for GetTempPath to give us
|
||||
* an answer that doesn't end with \. But let's make sure. */
|
||||
extra_backslash = "\\";
|
||||
}
|
||||
tor_snprintf(temp_dir, sizeof(temp_dir),
|
||||
"%s%stor_test_%d_%s", tmp, extra_backslash,
|
||||
(int)getpid(), rnd32);
|
||||
r = mkdir(temp_dir);
|
||||
}
|
||||
#else
|
||||
tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s",
|
||||
(int) getpid(), rnd32);
|
||||
r = mkdir(temp_dir, 0700);
|
||||
if (!r) {
|
||||
/* undo sticky bit so tests don't get confused. */
|
||||
r = chown(temp_dir, getuid(), getgid());
|
||||
}
|
||||
#endif
|
||||
if (r) {
|
||||
fprintf(stderr, "Can't create directory %s:", temp_dir);
|
||||
perror("");
|
||||
exit(1);
|
||||
}
|
||||
is_setup = 1;
|
||||
temp_dir_setup_in_pid = getpid();
|
||||
}
|
||||
|
||||
/** Return a filename relative to our testing temporary directory */
|
||||
const char *
|
||||
get_fname(const char *name)
|
||||
{
|
||||
static char buf[1024];
|
||||
setup_directory();
|
||||
if (!name)
|
||||
return temp_dir;
|
||||
tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Remove a directory and all of its subdirectories */
|
||||
static void
|
||||
rm_rf(const char *dir)
|
||||
{
|
||||
struct stat st;
|
||||
smartlist_t *elements;
|
||||
|
||||
elements = tor_listdir(dir);
|
||||
if (elements) {
|
||||
SMARTLIST_FOREACH_BEGIN(elements, const char *, cp) {
|
||||
char *tmp = NULL;
|
||||
tor_asprintf(&tmp, "%s"PATH_SEPARATOR"%s", dir, cp);
|
||||
if (0 == stat(tmp,&st) && (st.st_mode & S_IFDIR)) {
|
||||
rm_rf(tmp);
|
||||
} else {
|
||||
if (unlink(tmp)) {
|
||||
fprintf(stderr, "Error removing %s: %s\n", tmp, strerror(errno));
|
||||
}
|
||||
}
|
||||
tor_free(tmp);
|
||||
} SMARTLIST_FOREACH_END(cp);
|
||||
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
|
||||
smartlist_free(elements);
|
||||
}
|
||||
if (rmdir(dir))
|
||||
fprintf(stderr, "Error removing directory %s: %s\n", dir, strerror(errno));
|
||||
}
|
||||
|
||||
/** Remove all files stored under the temporary directory, and the directory
|
||||
* itself. Called by atexit(). */
|
||||
static void
|
||||
remove_directory(void)
|
||||
{
|
||||
if (getpid() != temp_dir_setup_in_pid) {
|
||||
/* Only clean out the tempdir when the main process is exiting. */
|
||||
return;
|
||||
}
|
||||
|
||||
rm_rf(temp_dir);
|
||||
}
|
||||
|
||||
/** Define this if unit tests spend too much time generating public keys*/
|
||||
#undef CACHE_GENERATED_KEYS
|
||||
|
||||
static crypto_pk_t *pregen_keys[5] = {NULL, NULL, NULL, NULL, NULL};
|
||||
#define N_PREGEN_KEYS ARRAY_LENGTH(pregen_keys)
|
||||
|
||||
/** Generate and return a new keypair for use in unit tests. If we're using
|
||||
* the key cache optimization, we might reuse keys: we only guarantee that
|
||||
* keys made with distinct values for <b>idx</b> are different. The value of
|
||||
* <b>idx</b> must be at least 0, and less than N_PREGEN_KEYS. */
|
||||
crypto_pk_t *
|
||||
pk_generate(int idx)
|
||||
{
|
||||
#ifdef CACHE_GENERATED_KEYS
|
||||
tor_assert(idx < N_PREGEN_KEYS);
|
||||
if (! pregen_keys[idx]) {
|
||||
pregen_keys[idx] = crypto_pk_new();
|
||||
tor_assert(!crypto_pk_generate_key(pregen_keys[idx]));
|
||||
}
|
||||
return crypto_pk_dup_key(pregen_keys[idx]);
|
||||
#else
|
||||
crypto_pk_t *result;
|
||||
(void) idx;
|
||||
result = crypto_pk_new();
|
||||
tor_assert(!crypto_pk_generate_key(result));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Free all storage used for the cached key optimization. */
|
||||
static void
|
||||
free_pregenerated_keys(void)
|
||||
{
|
||||
unsigned idx;
|
||||
for (idx = 0; idx < N_PREGEN_KEYS; ++idx) {
|
||||
if (pregen_keys[idx]) {
|
||||
crypto_pk_free(pregen_keys[idx]);
|
||||
pregen_keys[idx] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Run unit tests for the onion handshake code. */
|
||||
static void
|
||||
test_onion_handshake(void *arg)
|
||||
@ -1258,23 +1089,6 @@ test_stats(void *arg)
|
||||
tor_free(s);
|
||||
}
|
||||
|
||||
static void *
|
||||
passthrough_test_setup(const struct testcase_t *testcase)
|
||||
{
|
||||
return testcase->setup_data;
|
||||
}
|
||||
static int
|
||||
passthrough_test_cleanup(const struct testcase_t *testcase, void *ptr)
|
||||
{
|
||||
(void)testcase;
|
||||
(void)ptr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct testcase_setup_t passthrough_setup = {
|
||||
passthrough_test_setup, passthrough_test_cleanup
|
||||
};
|
||||
|
||||
#define ENT(name) \
|
||||
{ #name, test_ ## name , 0, NULL, NULL }
|
||||
#define FORK(name) \
|
||||
@ -1335,7 +1149,7 @@ extern struct testcase_t status_tests[];
|
||||
extern struct testcase_t thread_tests[];
|
||||
extern struct testcase_t util_tests[];
|
||||
|
||||
static struct testgroup_t testgroups[] = {
|
||||
struct testgroup_t testgroups[] = {
|
||||
{ "", test_array },
|
||||
{ "accounting/", accounting_tests },
|
||||
{ "addr/", addr_tests },
|
||||
@ -1379,86 +1193,3 @@ static struct testgroup_t testgroups[] = {
|
||||
END_OF_GROUPS
|
||||
};
|
||||
|
||||
/** Main entry point for unit test code: parse the command line, and run
|
||||
* some unit tests. */
|
||||
int
|
||||
main(int c, const char **v)
|
||||
{
|
||||
or_options_t *options;
|
||||
char *errmsg = NULL;
|
||||
int i, i_out;
|
||||
int loglevel = LOG_ERR;
|
||||
int accel_crypto = 0;
|
||||
|
||||
#ifdef USE_DMALLOC
|
||||
{
|
||||
int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_);
|
||||
tor_assert(r);
|
||||
}
|
||||
#endif
|
||||
|
||||
update_approx_time(time(NULL));
|
||||
options = options_new();
|
||||
tor_threads_init();
|
||||
init_logging(1);
|
||||
configure_backtrace_handler(get_version());
|
||||
|
||||
for (i_out = i = 1; i < c; ++i) {
|
||||
if (!strcmp(v[i], "--warn")) {
|
||||
loglevel = LOG_WARN;
|
||||
} else if (!strcmp(v[i], "--notice")) {
|
||||
loglevel = LOG_NOTICE;
|
||||
} else if (!strcmp(v[i], "--info")) {
|
||||
loglevel = LOG_INFO;
|
||||
} else if (!strcmp(v[i], "--debug")) {
|
||||
loglevel = LOG_DEBUG;
|
||||
} else if (!strcmp(v[i], "--accel")) {
|
||||
accel_crypto = 1;
|
||||
} else {
|
||||
v[i_out++] = v[i];
|
||||
}
|
||||
}
|
||||
c = i_out;
|
||||
|
||||
{
|
||||
log_severity_list_t s;
|
||||
memset(&s, 0, sizeof(s));
|
||||
set_log_severity_config(loglevel, LOG_ERR, &s);
|
||||
add_stream_log(&s, "", fileno(stdout));
|
||||
}
|
||||
|
||||
options->command = CMD_RUN_UNITTESTS;
|
||||
if (crypto_global_init(accel_crypto, NULL, NULL)) {
|
||||
printf("Can't initialize crypto subsystem; exiting.\n");
|
||||
return 1;
|
||||
}
|
||||
crypto_set_tls_dh_prime(NULL);
|
||||
crypto_seed_rng(1);
|
||||
rep_hist_init();
|
||||
network_init();
|
||||
setup_directory();
|
||||
options_init(options);
|
||||
options->DataDirectory = tor_strdup(temp_dir);
|
||||
options->EntryStatistics = 1;
|
||||
if (set_options(options, &errmsg) < 0) {
|
||||
printf("Failed to set initial options: %s\n", errmsg);
|
||||
tor_free(errmsg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
atexit(remove_directory);
|
||||
|
||||
have_failed = (tinytest_main(c, v, testgroups) != 0);
|
||||
|
||||
free_pregenerated_keys();
|
||||
#ifdef USE_DMALLOC
|
||||
tor_free_all(0);
|
||||
dmalloc_log_unfreed();
|
||||
#endif
|
||||
|
||||
if (have_failed)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
#include "orconfig.h"
|
||||
#define CRYPTO_CURVE25519_PRIVATE
|
||||
#define CRYPTO_S2K_PRIVATE
|
||||
#include "or.h"
|
||||
#include "test.h"
|
||||
#include "aes.h"
|
||||
@ -14,8 +13,6 @@
|
||||
#include "crypto_curve25519.h"
|
||||
#include "crypto_ed25519.h"
|
||||
#include "ed25519_vectors.inc"
|
||||
#include "crypto_s2k.h"
|
||||
#include "crypto_pwbox.h"
|
||||
|
||||
extern const char AUTHORITY_SIGNKEY_3[];
|
||||
extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
|
||||
@ -712,379 +709,6 @@ test_crypto_formats(void *arg)
|
||||
tor_free(data3);
|
||||
}
|
||||
|
||||
/** Run unit tests for our secret-to-key passphrase hashing functionality. */
|
||||
static void
|
||||
test_crypto_s2k_rfc2440(void *arg)
|
||||
{
|
||||
char buf[29];
|
||||
char buf2[29];
|
||||
char *buf3 = NULL;
|
||||
int i;
|
||||
|
||||
(void)arg;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memset(buf2, 0, sizeof(buf2));
|
||||
buf3 = tor_malloc(65536);
|
||||
memset(buf3, 0, 65536);
|
||||
|
||||
secret_to_key_rfc2440(buf+9, 20, "", 0, buf);
|
||||
crypto_digest(buf2+9, buf3, 1024);
|
||||
tt_mem_op(buf,OP_EQ, buf2, 29);
|
||||
|
||||
memcpy(buf,"vrbacrda",8);
|
||||
memcpy(buf2,"vrbacrda",8);
|
||||
buf[8] = 96;
|
||||
buf2[8] = 96;
|
||||
secret_to_key_rfc2440(buf+9, 20, "12345678", 8, buf);
|
||||
for (i = 0; i < 65536; i += 16) {
|
||||
memcpy(buf3+i, "vrbacrda12345678", 16);
|
||||
}
|
||||
crypto_digest(buf2+9, buf3, 65536);
|
||||
tt_mem_op(buf,OP_EQ, buf2, 29);
|
||||
|
||||
done:
|
||||
tor_free(buf3);
|
||||
}
|
||||
|
||||
static void
|
||||
run_s2k_tests(const unsigned flags, const unsigned type,
|
||||
int speclen, const int keylen, int legacy)
|
||||
{
|
||||
uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN], buf3[S2K_MAXLEN];
|
||||
int r;
|
||||
size_t sz;
|
||||
const char pw1[] = "You can't come in here unless you say swordfish!";
|
||||
const char pw2[] = "Now, I give you one more guess.";
|
||||
|
||||
r = secret_to_key_new(buf, sizeof(buf), &sz,
|
||||
pw1, strlen(pw1), flags);
|
||||
tt_int_op(r, OP_EQ, S2K_OKAY);
|
||||
tt_int_op(buf[0], OP_EQ, type);
|
||||
|
||||
tt_int_op(sz, OP_EQ, keylen + speclen);
|
||||
|
||||
if (legacy) {
|
||||
memmove(buf, buf+1, sz-1);
|
||||
--sz;
|
||||
--speclen;
|
||||
}
|
||||
|
||||
tt_int_op(S2K_OKAY, OP_EQ,
|
||||
secret_to_key_check(buf, sz, pw1, strlen(pw1)));
|
||||
|
||||
tt_int_op(S2K_BAD_SECRET, OP_EQ,
|
||||
secret_to_key_check(buf, sz, pw2, strlen(pw2)));
|
||||
|
||||
/* Move key to buf2, and clear it. */
|
||||
memset(buf3, 0, sizeof(buf3));
|
||||
memcpy(buf2, buf+speclen, keylen);
|
||||
memset(buf+speclen, 0, sz - speclen);
|
||||
|
||||
/* Derivekey should produce the same results. */
|
||||
tt_int_op(S2K_OKAY, OP_EQ,
|
||||
secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1)));
|
||||
|
||||
tt_mem_op(buf2, OP_EQ, buf3, keylen);
|
||||
|
||||
/* Derivekey with a longer output should fill the output. */
|
||||
memset(buf2, 0, sizeof(buf2));
|
||||
tt_int_op(S2K_OKAY, OP_EQ,
|
||||
secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen,
|
||||
pw1, strlen(pw1)));
|
||||
|
||||
tt_mem_op(buf2, OP_NE, buf3, sizeof(buf2));
|
||||
|
||||
memset(buf3, 0, sizeof(buf3));
|
||||
tt_int_op(S2K_OKAY, OP_EQ,
|
||||
secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen,
|
||||
pw1, strlen(pw1)));
|
||||
tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3));
|
||||
tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));
|
||||
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_s2k_general(void *arg)
|
||||
{
|
||||
const char *which = arg;
|
||||
|
||||
if (!strcmp(which, "scrypt")) {
|
||||
run_s2k_tests(0, 2, 19, 32, 0);
|
||||
} else if (!strcmp(which, "scrypt-low")) {
|
||||
run_s2k_tests(S2K_FLAG_LOW_MEM, 2, 19, 32, 0);
|
||||
} else if (!strcmp(which, "pbkdf2")) {
|
||||
run_s2k_tests(S2K_FLAG_USE_PBKDF2, 1, 18, 20, 0);
|
||||
} else if (!strcmp(which, "rfc2440")) {
|
||||
run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 0);
|
||||
} else if (!strcmp(which, "rfc2440-legacy")) {
|
||||
run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 1);
|
||||
} else {
|
||||
tt_fail();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_s2k_errors(void *arg)
|
||||
{
|
||||
uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN];
|
||||
size_t sz;
|
||||
|
||||
(void)arg;
|
||||
|
||||
/* Bogus specifiers: simple */
|
||||
tt_int_op(S2K_BAD_LEN, OP_EQ,
|
||||
secret_to_key_derivekey(buf, sizeof(buf),
|
||||
(const uint8_t*)"", 0, "ABC", 3));
|
||||
tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
|
||||
secret_to_key_derivekey(buf, sizeof(buf),
|
||||
(const uint8_t*)"\x10", 1, "ABC", 3));
|
||||
tt_int_op(S2K_BAD_LEN, OP_EQ,
|
||||
secret_to_key_derivekey(buf, sizeof(buf),
|
||||
(const uint8_t*)"\x01\x02", 2, "ABC", 3));
|
||||
|
||||
tt_int_op(S2K_BAD_LEN, OP_EQ,
|
||||
secret_to_key_check((const uint8_t*)"", 0, "ABC", 3));
|
||||
tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
|
||||
secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3));
|
||||
tt_int_op(S2K_BAD_LEN, OP_EQ,
|
||||
secret_to_key_check((const uint8_t*)"\x01\x02", 2, "ABC", 3));
|
||||
|
||||
/* too long gets "BAD_LEN" too */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 2;
|
||||
tt_int_op(S2K_BAD_LEN, OP_EQ,
|
||||
secret_to_key_derivekey(buf2, sizeof(buf2),
|
||||
buf, sizeof(buf), "ABC", 3));
|
||||
|
||||
/* Truncated output */
|
||||
#ifdef HAVE_LIBSCRYPT_H
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
|
||||
"ABC", 3, 0));
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
|
||||
"ABC", 3, S2K_FLAG_LOW_MEM));
|
||||
#endif
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 37, &sz,
|
||||
"ABC", 3, S2K_FLAG_USE_PBKDF2));
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 29, &sz,
|
||||
"ABC", 3, S2K_FLAG_NO_SCRYPT));
|
||||
|
||||
#ifdef HAVE_LIBSCRYPT_H
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, 0));
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18,
|
||||
S2K_FLAG_LOW_MEM));
|
||||
#endif
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 17,
|
||||
S2K_FLAG_USE_PBKDF2));
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 9,
|
||||
S2K_FLAG_NO_SCRYPT));
|
||||
|
||||
/* Now try using type-specific bogus specifiers. */
|
||||
|
||||
/* It's a bad pbkdf2 buffer if it has an iteration count that would overflow
|
||||
* int32_t. */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 1; /* pbkdf2 */
|
||||
buf[17] = 100; /* 1<<100 is much bigger than INT32_MAX */
|
||||
tt_int_op(S2K_BAD_PARAMS, OP_EQ,
|
||||
secret_to_key_derivekey(buf2, sizeof(buf2),
|
||||
buf, 18, "ABC", 3));
|
||||
|
||||
#ifdef HAVE_LIBSCRYPT_H
|
||||
/* It's a bad scrypt buffer if N would overflow uint64 */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 2; /* scrypt */
|
||||
buf[17] = 100; /* 1<<100 is much bigger than UINT64_MAX */
|
||||
tt_int_op(S2K_BAD_PARAMS, OP_EQ,
|
||||
secret_to_key_derivekey(buf2, sizeof(buf2),
|
||||
buf, 19, "ABC", 3));
|
||||
#endif
|
||||
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_scrypt_vectors(void *arg)
|
||||
{
|
||||
char *mem_op_hex_tmp = NULL;
|
||||
uint8_t spec[64], out[64];
|
||||
|
||||
(void)arg;
|
||||
#ifndef HAVE_LIBSCRYPT_H
|
||||
if (1)
|
||||
tt_skip();
|
||||
#endif
|
||||
|
||||
/* Test vectors from
|
||||
http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00 section 11.
|
||||
|
||||
Note that the names of 'r' and 'N' are switched in that section. Or
|
||||
possibly in libscrypt.
|
||||
*/
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"0400", 4);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(64, OP_EQ,
|
||||
secret_to_key_compute_key(out, 64, spec, 2, "", 0, 2));
|
||||
test_memeq_hex(out,
|
||||
"77d6576238657b203b19ca42c18a0497"
|
||||
"f16b4844e3074ae8dfdffa3fede21442"
|
||||
"fcd0069ded0948f8326a753a0fc81f17"
|
||||
"e8d3e0fb2e0d3628cf35e20c38d18906");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"4e61436c" "0A34", 12);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(64, OP_EQ,
|
||||
secret_to_key_compute_key(out, 64, spec, 6, "password", 8, 2));
|
||||
test_memeq_hex(out,
|
||||
"fdbabe1c9d3472007856e7190d01e9fe"
|
||||
"7c6ad7cbc8237830e77376634b373162"
|
||||
"2eaf30d92e22a3886ff109279d9830da"
|
||||
"c727afb94a83ee6d8360cbdfa2cc0640");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"536f6469756d43686c6f72696465" "0e30", 32);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(64, OP_EQ,
|
||||
secret_to_key_compute_key(out, 64, spec, 16,
|
||||
"pleaseletmein", 13, 2));
|
||||
test_memeq_hex(out,
|
||||
"7023bdcb3afd7348461c06cd81fd38eb"
|
||||
"fda8fbba904f8e3ea9b543f6545da1f2"
|
||||
"d5432955613f0fcf62d49705242a9af9"
|
||||
"e61e85dc0d651e40dfcf017b45575887");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"536f6469756d43686c6f72696465" "1430", 32);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(64, OP_EQ,
|
||||
secret_to_key_compute_key(out, 64, spec, 16,
|
||||
"pleaseletmein", 13, 2));
|
||||
test_memeq_hex(out,
|
||||
"2101cb9b6a511aaeaddbbe09cf70f881"
|
||||
"ec568d574a2ffd4dabe5ee9820adaa47"
|
||||
"8e56fd8f4ba5d09ffa1c6d927c40f4c3"
|
||||
"37304049e8a952fbcbf45c6fa77a41a4");
|
||||
|
||||
done:
|
||||
tor_free(mem_op_hex_tmp);
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_pbkdf2_vectors(void *arg)
|
||||
{
|
||||
char *mem_op_hex_tmp = NULL;
|
||||
uint8_t spec[64], out[64];
|
||||
(void)arg;
|
||||
|
||||
/* Test vectors from RFC6070, section 2 */
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"73616c74" "00" , 10);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(20, OP_EQ,
|
||||
secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
|
||||
test_memeq_hex(out, "0c60c80f961f0e71f3a9b524af6012062fe037a6");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"73616c74" "01" , 10);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(20, OP_EQ,
|
||||
secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
|
||||
test_memeq_hex(out, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"73616c74" "0C" , 10);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(20, OP_EQ,
|
||||
secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
|
||||
test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"73616c74" "18" , 10);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(20, OP_EQ,
|
||||
secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
|
||||
test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"73616c7453414c5473616c7453414c5473616c745"
|
||||
"3414c5473616c7453414c5473616c74" "0C" , 74);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(25, OP_EQ,
|
||||
secret_to_key_compute_key(out, 25, spec, 37,
|
||||
"passwordPASSWORDpassword", 24, 1));
|
||||
test_memeq_hex(out, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"7361006c74" "0c" , 12);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(16, OP_EQ,
|
||||
secret_to_key_compute_key(out, 16, spec, 6, "pass\0word", 9, 1));
|
||||
test_memeq_hex(out, "56fa6aa75548099dcc37d7f03425e0c3");
|
||||
|
||||
done:
|
||||
tor_free(mem_op_hex_tmp);
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_pwbox(void *arg)
|
||||
{
|
||||
uint8_t *boxed=NULL, *decoded=NULL;
|
||||
size_t len, dlen;
|
||||
unsigned i;
|
||||
const char msg[] = "This bunny reminds you that you still have a "
|
||||
"salamander in your sylladex. She is holding the bunny Dave got you. "
|
||||
"It’s sort of uncanny how similar they are, aside from the knitted "
|
||||
"enhancements. Seriously, what are the odds?? So weird.";
|
||||
const char pw[] = "I'm a night owl and a wise bird too";
|
||||
|
||||
const unsigned flags[] = { 0,
|
||||
S2K_FLAG_NO_SCRYPT,
|
||||
S2K_FLAG_LOW_MEM,
|
||||
S2K_FLAG_NO_SCRYPT|S2K_FLAG_LOW_MEM,
|
||||
S2K_FLAG_USE_PBKDF2 };
|
||||
(void)arg;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(flags); ++i) {
|
||||
tt_int_op(0, OP_EQ, crypto_pwbox(&boxed, &len,
|
||||
(const uint8_t*)msg, strlen(msg),
|
||||
pw, strlen(pw), flags[i]));
|
||||
tt_assert(boxed);
|
||||
tt_assert(len > 128+32);
|
||||
|
||||
tt_int_op(0, OP_EQ, crypto_unpwbox(&decoded, &dlen, boxed, len,
|
||||
pw, strlen(pw)));
|
||||
|
||||
tt_assert(decoded);
|
||||
tt_uint_op(dlen, OP_EQ, strlen(msg));
|
||||
tt_mem_op(decoded, OP_EQ, msg, dlen);
|
||||
|
||||
tor_free(decoded);
|
||||
|
||||
tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
|
||||
boxed, len,
|
||||
pw, strlen(pw)-1));
|
||||
boxed[len-1] ^= 1;
|
||||
tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
|
||||
boxed, len,
|
||||
pw, strlen(pw)));
|
||||
boxed[0] = 255;
|
||||
tt_int_op(UNPWBOX_CORRUPTED, OP_EQ, crypto_unpwbox(&decoded, &dlen,
|
||||
boxed, len,
|
||||
pw, strlen(pw)));
|
||||
|
||||
tor_free(boxed);
|
||||
}
|
||||
|
||||
done:
|
||||
tor_free(boxed);
|
||||
tor_free(decoded);
|
||||
}
|
||||
|
||||
/** Test AES-CTR encryption and decryption with IV. */
|
||||
static void
|
||||
test_crypto_aes_iv(void *arg)
|
||||
@ -1988,23 +1612,6 @@ struct testcase_t crypto_tests[] = {
|
||||
{ "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
|
||||
CRYPTO_LEGACY(digests),
|
||||
CRYPTO_LEGACY(dh),
|
||||
CRYPTO_LEGACY(s2k_rfc2440),
|
||||
#ifdef HAVE_LIBSCRYPT_H
|
||||
{ "s2k_scrypt", test_crypto_s2k_general, 0, &passthrough_setup,
|
||||
(void*)"scrypt" },
|
||||
{ "s2k_scrypt_low", test_crypto_s2k_general, 0, &passthrough_setup,
|
||||
(void*)"scrypt-low" },
|
||||
#endif
|
||||
{ "s2k_pbkdf2", test_crypto_s2k_general, 0, &passthrough_setup,
|
||||
(void*)"pbkdf2" },
|
||||
{ "s2k_rfc2440_general", test_crypto_s2k_general, 0, &passthrough_setup,
|
||||
(void*)"rfc2440" },
|
||||
{ "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &passthrough_setup,
|
||||
(void*)"rfc2440-legacy" },
|
||||
{ "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL },
|
||||
{ "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL },
|
||||
{ "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL },
|
||||
{ "pwbox", test_crypto_pwbox, 0, NULL, NULL },
|
||||
{ "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &passthrough_setup,
|
||||
(void*)"aes" },
|
||||
{ "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &passthrough_setup,
|
||||
|
410
src/test/test_crypto_slow.c
Normal file
410
src/test/test_crypto_slow.c
Normal file
@ -0,0 +1,410 @@
|
||||
/* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2015, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#include "orconfig.h"
|
||||
#define CRYPTO_S2K_PRIVATE
|
||||
#include "or.h"
|
||||
#include "test.h"
|
||||
#include "crypto_s2k.h"
|
||||
#include "crypto_pwbox.h"
|
||||
|
||||
|
||||
/** Run unit tests for our secret-to-key passphrase hashing functionality. */
|
||||
static void
|
||||
test_crypto_s2k_rfc2440(void *arg)
|
||||
{
|
||||
char buf[29];
|
||||
char buf2[29];
|
||||
char *buf3 = NULL;
|
||||
int i;
|
||||
|
||||
(void)arg;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memset(buf2, 0, sizeof(buf2));
|
||||
buf3 = tor_malloc(65536);
|
||||
memset(buf3, 0, 65536);
|
||||
|
||||
secret_to_key_rfc2440(buf+9, 20, "", 0, buf);
|
||||
crypto_digest(buf2+9, buf3, 1024);
|
||||
tt_mem_op(buf,OP_EQ, buf2, 29);
|
||||
|
||||
memcpy(buf,"vrbacrda",8);
|
||||
memcpy(buf2,"vrbacrda",8);
|
||||
buf[8] = 96;
|
||||
buf2[8] = 96;
|
||||
secret_to_key_rfc2440(buf+9, 20, "12345678", 8, buf);
|
||||
for (i = 0; i < 65536; i += 16) {
|
||||
memcpy(buf3+i, "vrbacrda12345678", 16);
|
||||
}
|
||||
crypto_digest(buf2+9, buf3, 65536);
|
||||
tt_mem_op(buf,OP_EQ, buf2, 29);
|
||||
|
||||
done:
|
||||
tor_free(buf3);
|
||||
}
|
||||
|
||||
static void
|
||||
run_s2k_tests(const unsigned flags, const unsigned type,
|
||||
int speclen, const int keylen, int legacy)
|
||||
{
|
||||
uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN], buf3[S2K_MAXLEN];
|
||||
int r;
|
||||
size_t sz;
|
||||
const char pw1[] = "You can't come in here unless you say swordfish!";
|
||||
const char pw2[] = "Now, I give you one more guess.";
|
||||
|
||||
r = secret_to_key_new(buf, sizeof(buf), &sz,
|
||||
pw1, strlen(pw1), flags);
|
||||
tt_int_op(r, OP_EQ, S2K_OKAY);
|
||||
tt_int_op(buf[0], OP_EQ, type);
|
||||
|
||||
tt_int_op(sz, OP_EQ, keylen + speclen);
|
||||
|
||||
if (legacy) {
|
||||
memmove(buf, buf+1, sz-1);
|
||||
--sz;
|
||||
--speclen;
|
||||
}
|
||||
|
||||
tt_int_op(S2K_OKAY, OP_EQ,
|
||||
secret_to_key_check(buf, sz, pw1, strlen(pw1)));
|
||||
|
||||
tt_int_op(S2K_BAD_SECRET, OP_EQ,
|
||||
secret_to_key_check(buf, sz, pw2, strlen(pw2)));
|
||||
|
||||
/* Move key to buf2, and clear it. */
|
||||
memset(buf3, 0, sizeof(buf3));
|
||||
memcpy(buf2, buf+speclen, keylen);
|
||||
memset(buf+speclen, 0, sz - speclen);
|
||||
|
||||
/* Derivekey should produce the same results. */
|
||||
tt_int_op(S2K_OKAY, OP_EQ,
|
||||
secret_to_key_derivekey(buf3, keylen, buf, speclen, pw1, strlen(pw1)));
|
||||
|
||||
tt_mem_op(buf2, OP_EQ, buf3, keylen);
|
||||
|
||||
/* Derivekey with a longer output should fill the output. */
|
||||
memset(buf2, 0, sizeof(buf2));
|
||||
tt_int_op(S2K_OKAY, OP_EQ,
|
||||
secret_to_key_derivekey(buf2, sizeof(buf2), buf, speclen,
|
||||
pw1, strlen(pw1)));
|
||||
|
||||
tt_mem_op(buf2, OP_NE, buf3, sizeof(buf2));
|
||||
|
||||
memset(buf3, 0, sizeof(buf3));
|
||||
tt_int_op(S2K_OKAY, OP_EQ,
|
||||
secret_to_key_derivekey(buf3, sizeof(buf3), buf, speclen,
|
||||
pw1, strlen(pw1)));
|
||||
tt_mem_op(buf2, OP_EQ, buf3, sizeof(buf3));
|
||||
tt_assert(!tor_mem_is_zero((char*)buf2+keylen, sizeof(buf2)-keylen));
|
||||
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_s2k_general(void *arg)
|
||||
{
|
||||
const char *which = arg;
|
||||
|
||||
if (!strcmp(which, "scrypt")) {
|
||||
run_s2k_tests(0, 2, 19, 32, 0);
|
||||
} else if (!strcmp(which, "scrypt-low")) {
|
||||
run_s2k_tests(S2K_FLAG_LOW_MEM, 2, 19, 32, 0);
|
||||
} else if (!strcmp(which, "pbkdf2")) {
|
||||
run_s2k_tests(S2K_FLAG_USE_PBKDF2, 1, 18, 20, 0);
|
||||
} else if (!strcmp(which, "rfc2440")) {
|
||||
run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 0);
|
||||
} else if (!strcmp(which, "rfc2440-legacy")) {
|
||||
run_s2k_tests(S2K_FLAG_NO_SCRYPT, 0, 10, 20, 1);
|
||||
} else {
|
||||
tt_fail();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_s2k_errors(void *arg)
|
||||
{
|
||||
uint8_t buf[S2K_MAXLEN], buf2[S2K_MAXLEN];
|
||||
size_t sz;
|
||||
|
||||
(void)arg;
|
||||
|
||||
/* Bogus specifiers: simple */
|
||||
tt_int_op(S2K_BAD_LEN, OP_EQ,
|
||||
secret_to_key_derivekey(buf, sizeof(buf),
|
||||
(const uint8_t*)"", 0, "ABC", 3));
|
||||
tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
|
||||
secret_to_key_derivekey(buf, sizeof(buf),
|
||||
(const uint8_t*)"\x10", 1, "ABC", 3));
|
||||
tt_int_op(S2K_BAD_LEN, OP_EQ,
|
||||
secret_to_key_derivekey(buf, sizeof(buf),
|
||||
(const uint8_t*)"\x01\x02", 2, "ABC", 3));
|
||||
|
||||
tt_int_op(S2K_BAD_LEN, OP_EQ,
|
||||
secret_to_key_check((const uint8_t*)"", 0, "ABC", 3));
|
||||
tt_int_op(S2K_BAD_ALGORITHM, OP_EQ,
|
||||
secret_to_key_check((const uint8_t*)"\x10", 1, "ABC", 3));
|
||||
tt_int_op(S2K_BAD_LEN, OP_EQ,
|
||||
secret_to_key_check((const uint8_t*)"\x01\x02", 2, "ABC", 3));
|
||||
|
||||
/* too long gets "BAD_LEN" too */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 2;
|
||||
tt_int_op(S2K_BAD_LEN, OP_EQ,
|
||||
secret_to_key_derivekey(buf2, sizeof(buf2),
|
||||
buf, sizeof(buf), "ABC", 3));
|
||||
|
||||
/* Truncated output */
|
||||
#ifdef HAVE_LIBSCRYPT_H
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
|
||||
"ABC", 3, 0));
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
|
||||
"ABC", 3, S2K_FLAG_LOW_MEM));
|
||||
#endif
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 37, &sz,
|
||||
"ABC", 3, S2K_FLAG_USE_PBKDF2));
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 29, &sz,
|
||||
"ABC", 3, S2K_FLAG_NO_SCRYPT));
|
||||
|
||||
#ifdef HAVE_LIBSCRYPT_H
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18, 0));
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 18,
|
||||
S2K_FLAG_LOW_MEM));
|
||||
#endif
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 17,
|
||||
S2K_FLAG_USE_PBKDF2));
|
||||
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_make_specifier(buf, 9,
|
||||
S2K_FLAG_NO_SCRYPT));
|
||||
|
||||
/* Now try using type-specific bogus specifiers. */
|
||||
|
||||
/* It's a bad pbkdf2 buffer if it has an iteration count that would overflow
|
||||
* int32_t. */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 1; /* pbkdf2 */
|
||||
buf[17] = 100; /* 1<<100 is much bigger than INT32_MAX */
|
||||
tt_int_op(S2K_BAD_PARAMS, OP_EQ,
|
||||
secret_to_key_derivekey(buf2, sizeof(buf2),
|
||||
buf, 18, "ABC", 3));
|
||||
|
||||
#ifdef HAVE_LIBSCRYPT_H
|
||||
/* It's a bad scrypt buffer if N would overflow uint64 */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = 2; /* scrypt */
|
||||
buf[17] = 100; /* 1<<100 is much bigger than UINT64_MAX */
|
||||
tt_int_op(S2K_BAD_PARAMS, OP_EQ,
|
||||
secret_to_key_derivekey(buf2, sizeof(buf2),
|
||||
buf, 19, "ABC", 3));
|
||||
#endif
|
||||
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_scrypt_vectors(void *arg)
|
||||
{
|
||||
char *mem_op_hex_tmp = NULL;
|
||||
uint8_t spec[64], out[64];
|
||||
|
||||
(void)arg;
|
||||
#ifndef HAVE_LIBSCRYPT_H
|
||||
if (1)
|
||||
tt_skip();
|
||||
#endif
|
||||
|
||||
/* Test vectors from
|
||||
http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00 section 11.
|
||||
|
||||
Note that the names of 'r' and 'N' are switched in that section. Or
|
||||
possibly in libscrypt.
|
||||
*/
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"0400", 4);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(64, OP_EQ,
|
||||
secret_to_key_compute_key(out, 64, spec, 2, "", 0, 2));
|
||||
test_memeq_hex(out,
|
||||
"77d6576238657b203b19ca42c18a0497"
|
||||
"f16b4844e3074ae8dfdffa3fede21442"
|
||||
"fcd0069ded0948f8326a753a0fc81f17"
|
||||
"e8d3e0fb2e0d3628cf35e20c38d18906");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"4e61436c" "0A34", 12);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(64, OP_EQ,
|
||||
secret_to_key_compute_key(out, 64, spec, 6, "password", 8, 2));
|
||||
test_memeq_hex(out,
|
||||
"fdbabe1c9d3472007856e7190d01e9fe"
|
||||
"7c6ad7cbc8237830e77376634b373162"
|
||||
"2eaf30d92e22a3886ff109279d9830da"
|
||||
"c727afb94a83ee6d8360cbdfa2cc0640");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"536f6469756d43686c6f72696465" "0e30", 32);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(64, OP_EQ,
|
||||
secret_to_key_compute_key(out, 64, spec, 16,
|
||||
"pleaseletmein", 13, 2));
|
||||
test_memeq_hex(out,
|
||||
"7023bdcb3afd7348461c06cd81fd38eb"
|
||||
"fda8fbba904f8e3ea9b543f6545da1f2"
|
||||
"d5432955613f0fcf62d49705242a9af9"
|
||||
"e61e85dc0d651e40dfcf017b45575887");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"536f6469756d43686c6f72696465" "1430", 32);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(64, OP_EQ,
|
||||
secret_to_key_compute_key(out, 64, spec, 16,
|
||||
"pleaseletmein", 13, 2));
|
||||
test_memeq_hex(out,
|
||||
"2101cb9b6a511aaeaddbbe09cf70f881"
|
||||
"ec568d574a2ffd4dabe5ee9820adaa47"
|
||||
"8e56fd8f4ba5d09ffa1c6d927c40f4c3"
|
||||
"37304049e8a952fbcbf45c6fa77a41a4");
|
||||
|
||||
done:
|
||||
tor_free(mem_op_hex_tmp);
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_pbkdf2_vectors(void *arg)
|
||||
{
|
||||
char *mem_op_hex_tmp = NULL;
|
||||
uint8_t spec[64], out[64];
|
||||
(void)arg;
|
||||
|
||||
/* Test vectors from RFC6070, section 2 */
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"73616c74" "00" , 10);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(20, OP_EQ,
|
||||
secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
|
||||
test_memeq_hex(out, "0c60c80f961f0e71f3a9b524af6012062fe037a6");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"73616c74" "01" , 10);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(20, OP_EQ,
|
||||
secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
|
||||
test_memeq_hex(out, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"73616c74" "0C" , 10);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(20, OP_EQ,
|
||||
secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
|
||||
test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"73616c74" "18" , 10);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(20, OP_EQ,
|
||||
secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
|
||||
test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"73616c7453414c5473616c7453414c5473616c745"
|
||||
"3414c5473616c7453414c5473616c74" "0C" , 74);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(25, OP_EQ,
|
||||
secret_to_key_compute_key(out, 25, spec, 37,
|
||||
"passwordPASSWORDpassword", 24, 1));
|
||||
test_memeq_hex(out, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038");
|
||||
|
||||
base16_decode((char*)spec, sizeof(spec),
|
||||
"7361006c74" "0c" , 12);
|
||||
memset(out, 0x00, sizeof(out));
|
||||
tt_int_op(16, OP_EQ,
|
||||
secret_to_key_compute_key(out, 16, spec, 6, "pass\0word", 9, 1));
|
||||
test_memeq_hex(out, "56fa6aa75548099dcc37d7f03425e0c3");
|
||||
|
||||
done:
|
||||
tor_free(mem_op_hex_tmp);
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_pwbox(void *arg)
|
||||
{
|
||||
uint8_t *boxed=NULL, *decoded=NULL;
|
||||
size_t len, dlen;
|
||||
unsigned i;
|
||||
const char msg[] = "This bunny reminds you that you still have a "
|
||||
"salamander in your sylladex. She is holding the bunny Dave got you. "
|
||||
"It’s sort of uncanny how similar they are, aside from the knitted "
|
||||
"enhancements. Seriously, what are the odds?? So weird.";
|
||||
const char pw[] = "I'm a night owl and a wise bird too";
|
||||
|
||||
const unsigned flags[] = { 0,
|
||||
S2K_FLAG_NO_SCRYPT,
|
||||
S2K_FLAG_LOW_MEM,
|
||||
S2K_FLAG_NO_SCRYPT|S2K_FLAG_LOW_MEM,
|
||||
S2K_FLAG_USE_PBKDF2 };
|
||||
(void)arg;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(flags); ++i) {
|
||||
tt_int_op(0, OP_EQ, crypto_pwbox(&boxed, &len,
|
||||
(const uint8_t*)msg, strlen(msg),
|
||||
pw, strlen(pw), flags[i]));
|
||||
tt_assert(boxed);
|
||||
tt_assert(len > 128+32);
|
||||
|
||||
tt_int_op(0, OP_EQ, crypto_unpwbox(&decoded, &dlen, boxed, len,
|
||||
pw, strlen(pw)));
|
||||
|
||||
tt_assert(decoded);
|
||||
tt_uint_op(dlen, OP_EQ, strlen(msg));
|
||||
tt_mem_op(decoded, OP_EQ, msg, dlen);
|
||||
|
||||
tor_free(decoded);
|
||||
|
||||
tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
|
||||
boxed, len,
|
||||
pw, strlen(pw)-1));
|
||||
boxed[len-1] ^= 1;
|
||||
tt_int_op(UNPWBOX_BAD_SECRET, OP_EQ, crypto_unpwbox(&decoded, &dlen,
|
||||
boxed, len,
|
||||
pw, strlen(pw)));
|
||||
boxed[0] = 255;
|
||||
tt_int_op(UNPWBOX_CORRUPTED, OP_EQ, crypto_unpwbox(&decoded, &dlen,
|
||||
boxed, len,
|
||||
pw, strlen(pw)));
|
||||
|
||||
tor_free(boxed);
|
||||
}
|
||||
|
||||
done:
|
||||
tor_free(boxed);
|
||||
tor_free(decoded);
|
||||
}
|
||||
|
||||
#define CRYPTO_LEGACY(name) \
|
||||
{ #name, test_crypto_ ## name , 0, NULL, NULL }
|
||||
|
||||
struct testcase_t slow_crypto_tests[] = {
|
||||
CRYPTO_LEGACY(s2k_rfc2440),
|
||||
#ifdef HAVE_LIBSCRYPT_H
|
||||
{ "s2k_scrypt", test_crypto_s2k_general, 0, &passthrough_setup,
|
||||
(void*)"scrypt" },
|
||||
{ "s2k_scrypt_low", test_crypto_s2k_general, 0, &passthrough_setup,
|
||||
(void*)"scrypt-low" },
|
||||
#endif
|
||||
{ "s2k_pbkdf2", test_crypto_s2k_general, 0, &passthrough_setup,
|
||||
(void*)"pbkdf2" },
|
||||
{ "s2k_rfc2440_general", test_crypto_s2k_general, 0, &passthrough_setup,
|
||||
(void*)"rfc2440" },
|
||||
{ "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &passthrough_setup,
|
||||
(void*)"rfc2440-legacy" },
|
||||
{ "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL },
|
||||
{ "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL },
|
||||
{ "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL },
|
||||
{ "pwbox", test_crypto_pwbox, 0, NULL, NULL },
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
29
src/test/test_slow.c
Normal file
29
src/test/test_slow.c
Normal file
@ -0,0 +1,29 @@
|
||||
/* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2015, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file test_slow.c
|
||||
* \brief Slower unit tests for many pieces of the lower level Tor modules.
|
||||
**/
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "or.h"
|
||||
#include "test.h"
|
||||
|
||||
extern struct testcase_t slow_crypto_tests[];
|
||||
extern struct testcase_t slow_util_tests[];
|
||||
|
||||
struct testgroup_t testgroups[] = {
|
||||
{ "slow/crypto/", slow_crypto_tests },
|
||||
{ "slow/util/", slow_util_tests },
|
||||
END_OF_GROUPS
|
||||
};
|
||||
|
@ -3380,370 +3380,6 @@ test_util_fgets_eagain(void *ptr)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef BUILDDIR
|
||||
#define BUILDDIR "."
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define notify_pending_waitpid_callbacks() STMT_NIL
|
||||
#define TEST_CHILD "test-child.exe"
|
||||
#define EOL "\r\n"
|
||||
#else
|
||||
#define TEST_CHILD (BUILDDIR "/src/test/test-child")
|
||||
#define EOL "\n"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
/* I've assumed Windows doesn't have the gap between fork and exec
|
||||
* that causes the race condition on unix-like platforms */
|
||||
#define MATCH_PROCESS_STATUS(s1,s2) ((s1) == (s2))
|
||||
|
||||
#else
|
||||
/* work around a race condition of the timing of SIGCHLD handler updates
|
||||
* to the process_handle's fields, and checks of those fields
|
||||
*
|
||||
* TODO: Once we can signal failure to exec, change PROCESS_STATUS_RUNNING to
|
||||
* PROCESS_STATUS_ERROR (and similarly with *_OR_NOTRUNNING) */
|
||||
#define PROCESS_STATUS_RUNNING_OR_NOTRUNNING (PROCESS_STATUS_RUNNING+1)
|
||||
#define IS_RUNNING_OR_NOTRUNNING(s) \
|
||||
((s) == PROCESS_STATUS_RUNNING || (s) == PROCESS_STATUS_NOTRUNNING)
|
||||
/* well, this is ugly */
|
||||
#define MATCH_PROCESS_STATUS(s1,s2) \
|
||||
( (s1) == (s2) \
|
||||
||((s1) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \
|
||||
&& IS_RUNNING_OR_NOTRUNNING(s2)) \
|
||||
||((s2) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \
|
||||
&& IS_RUNNING_OR_NOTRUNNING(s1)))
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
/** Helper function for testing tor_spawn_background */
|
||||
static void
|
||||
run_util_spawn_background(const char *argv[], const char *expected_out,
|
||||
const char *expected_err, int expected_exit,
|
||||
int expected_status)
|
||||
{
|
||||
int retval, exit_code;
|
||||
ssize_t pos;
|
||||
process_handle_t *process_handle=NULL;
|
||||
char stdout_buf[100], stderr_buf[100];
|
||||
int status;
|
||||
|
||||
/* Start the program */
|
||||
#ifdef _WIN32
|
||||
status = tor_spawn_background(NULL, argv, NULL, &process_handle);
|
||||
#else
|
||||
status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
|
||||
#endif
|
||||
|
||||
notify_pending_waitpid_callbacks();
|
||||
|
||||
/* the race condition doesn't affect status,
|
||||
* because status isn't updated by the SIGCHLD handler,
|
||||
* but we still need to handle PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
|
||||
tt_assert(MATCH_PROCESS_STATUS(expected_status, status));
|
||||
if (status == PROCESS_STATUS_ERROR) {
|
||||
tt_ptr_op(process_handle, OP_EQ, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
tt_assert(process_handle != NULL);
|
||||
|
||||
/* When a spawned process forks, fails, then exits very quickly,
|
||||
* (this typically occurs when exec fails)
|
||||
* there is a race condition between the SIGCHLD handler
|
||||
* updating the process_handle's fields, and this test
|
||||
* checking the process status in those fields.
|
||||
* The SIGCHLD update can occur before or after the code below executes.
|
||||
* This causes intermittent failures in spawn_background_fail(),
|
||||
* typically when the machine is under load.
|
||||
* We use PROCESS_STATUS_RUNNING_OR_NOTRUNNING to avoid this issue. */
|
||||
|
||||
/* the race condition affects the change in
|
||||
* process_handle->status from RUNNING to NOTRUNNING */
|
||||
tt_assert(MATCH_PROCESS_STATUS(expected_status, process_handle->status));
|
||||
|
||||
#ifndef _WIN32
|
||||
notify_pending_waitpid_callbacks();
|
||||
/* the race condition affects the change in
|
||||
* process_handle->waitpid_cb to NULL,
|
||||
* so we skip the check if expected_status is ambiguous,
|
||||
* that is, PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
|
||||
tt_assert(process_handle->waitpid_cb != NULL
|
||||
|| expected_status == PROCESS_STATUS_RUNNING_OR_NOTRUNNING);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
|
||||
tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
|
||||
#else
|
||||
tt_assert(process_handle->stdout_pipe >= 0);
|
||||
tt_assert(process_handle->stderr_pipe >= 0);
|
||||
#endif
|
||||
|
||||
/* Check stdout */
|
||||
pos = tor_read_all_from_process_stdout(process_handle, stdout_buf,
|
||||
sizeof(stdout_buf) - 1);
|
||||
tt_assert(pos >= 0);
|
||||
stdout_buf[pos] = '\0';
|
||||
tt_int_op(strlen(expected_out),OP_EQ, pos);
|
||||
tt_str_op(expected_out,OP_EQ, stdout_buf);
|
||||
|
||||
notify_pending_waitpid_callbacks();
|
||||
|
||||
/* Check it terminated correctly */
|
||||
retval = tor_get_exit_code(process_handle, 1, &exit_code);
|
||||
tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
|
||||
tt_int_op(expected_exit,OP_EQ, exit_code);
|
||||
// TODO: Make test-child exit with something other than 0
|
||||
|
||||
#ifndef _WIN32
|
||||
notify_pending_waitpid_callbacks();
|
||||
tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
|
||||
#endif
|
||||
|
||||
/* Check stderr */
|
||||
pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
|
||||
sizeof(stderr_buf) - 1);
|
||||
tt_assert(pos >= 0);
|
||||
stderr_buf[pos] = '\0';
|
||||
tt_str_op(expected_err,OP_EQ, stderr_buf);
|
||||
tt_int_op(strlen(expected_err),OP_EQ, pos);
|
||||
|
||||
notify_pending_waitpid_callbacks();
|
||||
|
||||
done:
|
||||
if (process_handle)
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
}
|
||||
|
||||
/** Check that we can launch a process and read the output */
|
||||
static void
|
||||
test_util_spawn_background_ok(void *ptr)
|
||||
{
|
||||
const char *argv[] = {TEST_CHILD, "--test", NULL};
|
||||
const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL;
|
||||
const char *expected_err = "ERR"EOL;
|
||||
|
||||
(void)ptr;
|
||||
|
||||
run_util_spawn_background(argv, expected_out, expected_err, 0,
|
||||
PROCESS_STATUS_RUNNING);
|
||||
}
|
||||
|
||||
/** Check that failing to find the executable works as expected */
|
||||
static void
|
||||
test_util_spawn_background_fail(void *ptr)
|
||||
{
|
||||
const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
|
||||
const char *expected_err = "";
|
||||
char expected_out[1024];
|
||||
char code[32];
|
||||
#ifdef _WIN32
|
||||
const int expected_status = PROCESS_STATUS_ERROR;
|
||||
#else
|
||||
/* TODO: Once we can signal failure to exec, set this to be
|
||||
* PROCESS_STATUS_RUNNING_OR_ERROR */
|
||||
const int expected_status = PROCESS_STATUS_RUNNING_OR_NOTRUNNING;
|
||||
#endif
|
||||
|
||||
memset(expected_out, 0xf0, sizeof(expected_out));
|
||||
memset(code, 0xf0, sizeof(code));
|
||||
|
||||
(void)ptr;
|
||||
|
||||
tor_snprintf(code, sizeof(code), "%x/%x",
|
||||
9 /* CHILD_STATE_FAILEXEC */ , ENOENT);
|
||||
tor_snprintf(expected_out, sizeof(expected_out),
|
||||
"ERR: Failed to spawn background process - code %s\n", code);
|
||||
|
||||
run_util_spawn_background(argv, expected_out, expected_err, 255,
|
||||
expected_status);
|
||||
}
|
||||
|
||||
/** Test that reading from a handle returns a partial read rather than
|
||||
* blocking */
|
||||
static void
|
||||
test_util_spawn_background_partial_read_impl(int exit_early)
|
||||
{
|
||||
const int expected_exit = 0;
|
||||
const int expected_status = PROCESS_STATUS_RUNNING;
|
||||
|
||||
int retval, exit_code;
|
||||
ssize_t pos = -1;
|
||||
process_handle_t *process_handle=NULL;
|
||||
int status;
|
||||
char stdout_buf[100], stderr_buf[100];
|
||||
|
||||
const char *argv[] = {TEST_CHILD, "--test", NULL};
|
||||
const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL,
|
||||
"DONE" EOL,
|
||||
NULL };
|
||||
const char *expected_err = "ERR" EOL;
|
||||
|
||||
#ifndef _WIN32
|
||||
int eof = 0;
|
||||
#endif
|
||||
int expected_out_ctr;
|
||||
|
||||
if (exit_early) {
|
||||
argv[1] = "--hang";
|
||||
expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL;
|
||||
}
|
||||
|
||||
/* Start the program */
|
||||
#ifdef _WIN32
|
||||
status = tor_spawn_background(NULL, argv, NULL, &process_handle);
|
||||
#else
|
||||
status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
|
||||
#endif
|
||||
tt_int_op(expected_status,OP_EQ, status);
|
||||
tt_assert(process_handle);
|
||||
tt_int_op(expected_status,OP_EQ, process_handle->status);
|
||||
|
||||
/* Check stdout */
|
||||
for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) {
|
||||
#ifdef _WIN32
|
||||
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
|
||||
sizeof(stdout_buf) - 1, NULL);
|
||||
#else
|
||||
/* Check that we didn't read the end of file last time */
|
||||
tt_assert(!eof);
|
||||
pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
|
||||
sizeof(stdout_buf) - 1, NULL, &eof);
|
||||
#endif
|
||||
log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
|
||||
|
||||
/* We would have blocked, keep on trying */
|
||||
if (0 == pos)
|
||||
continue;
|
||||
|
||||
tt_assert(pos > 0);
|
||||
stdout_buf[pos] = '\0';
|
||||
tt_str_op(expected_out[expected_out_ctr],OP_EQ, stdout_buf);
|
||||
tt_int_op(strlen(expected_out[expected_out_ctr]),OP_EQ, pos);
|
||||
expected_out_ctr++;
|
||||
}
|
||||
|
||||
if (exit_early) {
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
process_handle = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* The process should have exited without writing more */
|
||||
#ifdef _WIN32
|
||||
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
|
||||
sizeof(stdout_buf) - 1,
|
||||
process_handle);
|
||||
tt_int_op(0,OP_EQ, pos);
|
||||
#else
|
||||
if (!eof) {
|
||||
/* We should have got all the data, but maybe not the EOF flag */
|
||||
pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
|
||||
sizeof(stdout_buf) - 1,
|
||||
process_handle, &eof);
|
||||
tt_int_op(0,OP_EQ, pos);
|
||||
tt_assert(eof);
|
||||
}
|
||||
/* Otherwise, we got the EOF on the last read */
|
||||
#endif
|
||||
|
||||
/* Check it terminated correctly */
|
||||
retval = tor_get_exit_code(process_handle, 1, &exit_code);
|
||||
tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
|
||||
tt_int_op(expected_exit,OP_EQ, exit_code);
|
||||
|
||||
// TODO: Make test-child exit with something other than 0
|
||||
|
||||
/* Check stderr */
|
||||
pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
|
||||
sizeof(stderr_buf) - 1);
|
||||
tt_assert(pos >= 0);
|
||||
stderr_buf[pos] = '\0';
|
||||
tt_str_op(expected_err,OP_EQ, stderr_buf);
|
||||
tt_int_op(strlen(expected_err),OP_EQ, pos);
|
||||
|
||||
done:
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_spawn_background_partial_read(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
test_util_spawn_background_partial_read_impl(0);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_spawn_background_exit_early(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
test_util_spawn_background_partial_read_impl(1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_spawn_background_waitpid_notify(void *arg)
|
||||
{
|
||||
int retval, exit_code;
|
||||
process_handle_t *process_handle=NULL;
|
||||
int status;
|
||||
int ms_timer;
|
||||
|
||||
const char *argv[] = {TEST_CHILD, "--fast", NULL};
|
||||
|
||||
(void) arg;
|
||||
|
||||
#ifdef _WIN32
|
||||
status = tor_spawn_background(NULL, argv, NULL, &process_handle);
|
||||
#else
|
||||
status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
|
||||
#endif
|
||||
|
||||
tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
|
||||
tt_ptr_op(process_handle, OP_NE, NULL);
|
||||
|
||||
/* We're not going to look at the stdout/stderr output this time. Instead,
|
||||
* we're testing whether notify_pending_waitpid_calbacks() can report the
|
||||
* process exit (on unix) and/or whether tor_get_exit_code() can notice it
|
||||
* (on windows) */
|
||||
|
||||
#ifndef _WIN32
|
||||
ms_timer = 30*1000;
|
||||
tt_ptr_op(process_handle->waitpid_cb, OP_NE, NULL);
|
||||
while (process_handle->waitpid_cb && ms_timer > 0) {
|
||||
tor_sleep_msec(100);
|
||||
ms_timer -= 100;
|
||||
notify_pending_waitpid_callbacks();
|
||||
}
|
||||
tt_int_op(ms_timer, OP_GT, 0);
|
||||
tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
|
||||
#endif
|
||||
|
||||
ms_timer = 30*1000;
|
||||
while (((retval = tor_get_exit_code(process_handle, 0, &exit_code))
|
||||
== PROCESS_EXIT_RUNNING) && ms_timer > 0) {
|
||||
tor_sleep_msec(100);
|
||||
ms_timer -= 100;
|
||||
}
|
||||
tt_int_op(ms_timer, OP_GT, 0);
|
||||
|
||||
tt_int_op(retval, OP_EQ, PROCESS_EXIT_EXITED);
|
||||
|
||||
done:
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
}
|
||||
|
||||
#undef TEST_CHILD
|
||||
#undef EOL
|
||||
|
||||
#undef MATCH_PROCESS_STATUS
|
||||
|
||||
#ifndef _WIN32
|
||||
#undef PROCESS_STATUS_RUNNING_OR_NOTRUNNING
|
||||
#undef IS_RUNNING_OR_NOTRUNNING
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Test for format_hex_number_sigsafe()
|
||||
*/
|
||||
@ -4796,11 +4432,6 @@ struct testcase_t util_tests[] = {
|
||||
UTIL_TEST(exit_status, 0),
|
||||
UTIL_TEST(fgets_eagain, 0),
|
||||
#endif
|
||||
UTIL_TEST(spawn_background_ok, 0),
|
||||
UTIL_TEST(spawn_background_fail, 0),
|
||||
UTIL_TEST(spawn_background_partial_read, 0),
|
||||
UTIL_TEST(spawn_background_exit_early, 0),
|
||||
UTIL_TEST(spawn_background_waitpid_notify, 0),
|
||||
UTIL_TEST(format_hex_number, 0),
|
||||
UTIL_TEST(format_dec_number, 0),
|
||||
UTIL_TEST(join_win_cmdline, 0),
|
||||
|
388
src/test/test_util_slow.c
Normal file
388
src/test/test_util_slow.c
Normal file
@ -0,0 +1,388 @@
|
||||
/* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2015, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#include "orconfig.h"
|
||||
#define UTIL_PRIVATE
|
||||
#include "util.h"
|
||||
#include "util_process.h"
|
||||
#include "crypto.h"
|
||||
#include "torlog.h"
|
||||
#include "test.h"
|
||||
|
||||
#ifndef BUILDDIR
|
||||
#define BUILDDIR "."
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define notify_pending_waitpid_callbacks() STMT_NIL
|
||||
#define TEST_CHILD "test-child.exe"
|
||||
#define EOL "\r\n"
|
||||
#else
|
||||
#define TEST_CHILD (BUILDDIR "/src/test/test-child")
|
||||
#define EOL "\n"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
/* I've assumed Windows doesn't have the gap between fork and exec
|
||||
* that causes the race condition on unix-like platforms */
|
||||
#define MATCH_PROCESS_STATUS(s1,s2) ((s1) == (s2))
|
||||
|
||||
#else
|
||||
/* work around a race condition of the timing of SIGCHLD handler updates
|
||||
* to the process_handle's fields, and checks of those fields
|
||||
*
|
||||
* TODO: Once we can signal failure to exec, change PROCESS_STATUS_RUNNING to
|
||||
* PROCESS_STATUS_ERROR (and similarly with *_OR_NOTRUNNING) */
|
||||
#define PROCESS_STATUS_RUNNING_OR_NOTRUNNING (PROCESS_STATUS_RUNNING+1)
|
||||
#define IS_RUNNING_OR_NOTRUNNING(s) \
|
||||
((s) == PROCESS_STATUS_RUNNING || (s) == PROCESS_STATUS_NOTRUNNING)
|
||||
/* well, this is ugly */
|
||||
#define MATCH_PROCESS_STATUS(s1,s2) \
|
||||
( (s1) == (s2) \
|
||||
||((s1) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \
|
||||
&& IS_RUNNING_OR_NOTRUNNING(s2)) \
|
||||
||((s2) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \
|
||||
&& IS_RUNNING_OR_NOTRUNNING(s1)))
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
/** Helper function for testing tor_spawn_background */
|
||||
static void
|
||||
run_util_spawn_background(const char *argv[], const char *expected_out,
|
||||
const char *expected_err, int expected_exit,
|
||||
int expected_status)
|
||||
{
|
||||
int retval, exit_code;
|
||||
ssize_t pos;
|
||||
process_handle_t *process_handle=NULL;
|
||||
char stdout_buf[100], stderr_buf[100];
|
||||
int status;
|
||||
|
||||
/* Start the program */
|
||||
#ifdef _WIN32
|
||||
status = tor_spawn_background(NULL, argv, NULL, &process_handle);
|
||||
#else
|
||||
status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
|
||||
#endif
|
||||
|
||||
notify_pending_waitpid_callbacks();
|
||||
|
||||
/* the race condition doesn't affect status,
|
||||
* because status isn't updated by the SIGCHLD handler,
|
||||
* but we still need to handle PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
|
||||
tt_assert(MATCH_PROCESS_STATUS(expected_status, status));
|
||||
if (status == PROCESS_STATUS_ERROR) {
|
||||
tt_ptr_op(process_handle, OP_EQ, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
tt_assert(process_handle != NULL);
|
||||
|
||||
/* When a spawned process forks, fails, then exits very quickly,
|
||||
* (this typically occurs when exec fails)
|
||||
* there is a race condition between the SIGCHLD handler
|
||||
* updating the process_handle's fields, and this test
|
||||
* checking the process status in those fields.
|
||||
* The SIGCHLD update can occur before or after the code below executes.
|
||||
* This causes intermittent failures in spawn_background_fail(),
|
||||
* typically when the machine is under load.
|
||||
* We use PROCESS_STATUS_RUNNING_OR_NOTRUNNING to avoid this issue. */
|
||||
|
||||
/* the race condition affects the change in
|
||||
* process_handle->status from RUNNING to NOTRUNNING */
|
||||
tt_assert(MATCH_PROCESS_STATUS(expected_status, process_handle->status));
|
||||
|
||||
#ifndef _WIN32
|
||||
notify_pending_waitpid_callbacks();
|
||||
/* the race condition affects the change in
|
||||
* process_handle->waitpid_cb to NULL,
|
||||
* so we skip the check if expected_status is ambiguous,
|
||||
* that is, PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
|
||||
tt_assert(process_handle->waitpid_cb != NULL
|
||||
|| expected_status == PROCESS_STATUS_RUNNING_OR_NOTRUNNING);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
|
||||
tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
|
||||
#else
|
||||
tt_assert(process_handle->stdout_pipe >= 0);
|
||||
tt_assert(process_handle->stderr_pipe >= 0);
|
||||
#endif
|
||||
|
||||
/* Check stdout */
|
||||
pos = tor_read_all_from_process_stdout(process_handle, stdout_buf,
|
||||
sizeof(stdout_buf) - 1);
|
||||
tt_assert(pos >= 0);
|
||||
stdout_buf[pos] = '\0';
|
||||
tt_int_op(strlen(expected_out),OP_EQ, pos);
|
||||
tt_str_op(expected_out,OP_EQ, stdout_buf);
|
||||
|
||||
notify_pending_waitpid_callbacks();
|
||||
|
||||
/* Check it terminated correctly */
|
||||
retval = tor_get_exit_code(process_handle, 1, &exit_code);
|
||||
tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
|
||||
tt_int_op(expected_exit,OP_EQ, exit_code);
|
||||
// TODO: Make test-child exit with something other than 0
|
||||
|
||||
#ifndef _WIN32
|
||||
notify_pending_waitpid_callbacks();
|
||||
tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
|
||||
#endif
|
||||
|
||||
/* Check stderr */
|
||||
pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
|
||||
sizeof(stderr_buf) - 1);
|
||||
tt_assert(pos >= 0);
|
||||
stderr_buf[pos] = '\0';
|
||||
tt_str_op(expected_err,OP_EQ, stderr_buf);
|
||||
tt_int_op(strlen(expected_err),OP_EQ, pos);
|
||||
|
||||
notify_pending_waitpid_callbacks();
|
||||
|
||||
done:
|
||||
if (process_handle)
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
}
|
||||
|
||||
/** Check that we can launch a process and read the output */
|
||||
static void
|
||||
test_util_spawn_background_ok(void *ptr)
|
||||
{
|
||||
const char *argv[] = {TEST_CHILD, "--test", NULL};
|
||||
const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL;
|
||||
const char *expected_err = "ERR"EOL;
|
||||
|
||||
(void)ptr;
|
||||
|
||||
run_util_spawn_background(argv, expected_out, expected_err, 0,
|
||||
PROCESS_STATUS_RUNNING);
|
||||
}
|
||||
|
||||
/** Check that failing to find the executable works as expected */
|
||||
static void
|
||||
test_util_spawn_background_fail(void *ptr)
|
||||
{
|
||||
const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
|
||||
const char *expected_err = "";
|
||||
char expected_out[1024];
|
||||
char code[32];
|
||||
#ifdef _WIN32
|
||||
const int expected_status = PROCESS_STATUS_ERROR;
|
||||
#else
|
||||
/* TODO: Once we can signal failure to exec, set this to be
|
||||
* PROCESS_STATUS_RUNNING_OR_ERROR */
|
||||
const int expected_status = PROCESS_STATUS_RUNNING_OR_NOTRUNNING;
|
||||
#endif
|
||||
|
||||
memset(expected_out, 0xf0, sizeof(expected_out));
|
||||
memset(code, 0xf0, sizeof(code));
|
||||
|
||||
(void)ptr;
|
||||
|
||||
tor_snprintf(code, sizeof(code), "%x/%x",
|
||||
9 /* CHILD_STATE_FAILEXEC */ , ENOENT);
|
||||
tor_snprintf(expected_out, sizeof(expected_out),
|
||||
"ERR: Failed to spawn background process - code %s\n", code);
|
||||
|
||||
run_util_spawn_background(argv, expected_out, expected_err, 255,
|
||||
expected_status);
|
||||
}
|
||||
|
||||
/** Test that reading from a handle returns a partial read rather than
|
||||
* blocking */
|
||||
static void
|
||||
test_util_spawn_background_partial_read_impl(int exit_early)
|
||||
{
|
||||
const int expected_exit = 0;
|
||||
const int expected_status = PROCESS_STATUS_RUNNING;
|
||||
|
||||
int retval, exit_code;
|
||||
ssize_t pos = -1;
|
||||
process_handle_t *process_handle=NULL;
|
||||
int status;
|
||||
char stdout_buf[100], stderr_buf[100];
|
||||
|
||||
const char *argv[] = {TEST_CHILD, "--test", NULL};
|
||||
const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL,
|
||||
"DONE" EOL,
|
||||
NULL };
|
||||
const char *expected_err = "ERR" EOL;
|
||||
|
||||
#ifndef _WIN32
|
||||
int eof = 0;
|
||||
#endif
|
||||
int expected_out_ctr;
|
||||
|
||||
if (exit_early) {
|
||||
argv[1] = "--hang";
|
||||
expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL;
|
||||
}
|
||||
|
||||
/* Start the program */
|
||||
#ifdef _WIN32
|
||||
status = tor_spawn_background(NULL, argv, NULL, &process_handle);
|
||||
#else
|
||||
status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
|
||||
#endif
|
||||
tt_int_op(expected_status,OP_EQ, status);
|
||||
tt_assert(process_handle);
|
||||
tt_int_op(expected_status,OP_EQ, process_handle->status);
|
||||
|
||||
/* Check stdout */
|
||||
for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) {
|
||||
#ifdef _WIN32
|
||||
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
|
||||
sizeof(stdout_buf) - 1, NULL);
|
||||
#else
|
||||
/* Check that we didn't read the end of file last time */
|
||||
tt_assert(!eof);
|
||||
pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
|
||||
sizeof(stdout_buf) - 1, NULL, &eof);
|
||||
#endif
|
||||
log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
|
||||
|
||||
/* We would have blocked, keep on trying */
|
||||
if (0 == pos)
|
||||
continue;
|
||||
|
||||
tt_assert(pos > 0);
|
||||
stdout_buf[pos] = '\0';
|
||||
tt_str_op(expected_out[expected_out_ctr],OP_EQ, stdout_buf);
|
||||
tt_int_op(strlen(expected_out[expected_out_ctr]),OP_EQ, pos);
|
||||
expected_out_ctr++;
|
||||
}
|
||||
|
||||
if (exit_early) {
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
process_handle = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* The process should have exited without writing more */
|
||||
#ifdef _WIN32
|
||||
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
|
||||
sizeof(stdout_buf) - 1,
|
||||
process_handle);
|
||||
tt_int_op(0,OP_EQ, pos);
|
||||
#else
|
||||
if (!eof) {
|
||||
/* We should have got all the data, but maybe not the EOF flag */
|
||||
pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf,
|
||||
sizeof(stdout_buf) - 1,
|
||||
process_handle, &eof);
|
||||
tt_int_op(0,OP_EQ, pos);
|
||||
tt_assert(eof);
|
||||
}
|
||||
/* Otherwise, we got the EOF on the last read */
|
||||
#endif
|
||||
|
||||
/* Check it terminated correctly */
|
||||
retval = tor_get_exit_code(process_handle, 1, &exit_code);
|
||||
tt_int_op(PROCESS_EXIT_EXITED,OP_EQ, retval);
|
||||
tt_int_op(expected_exit,OP_EQ, exit_code);
|
||||
|
||||
// TODO: Make test-child exit with something other than 0
|
||||
|
||||
/* Check stderr */
|
||||
pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
|
||||
sizeof(stderr_buf) - 1);
|
||||
tt_assert(pos >= 0);
|
||||
stderr_buf[pos] = '\0';
|
||||
tt_str_op(expected_err,OP_EQ, stderr_buf);
|
||||
tt_int_op(strlen(expected_err),OP_EQ, pos);
|
||||
|
||||
done:
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_spawn_background_partial_read(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
test_util_spawn_background_partial_read_impl(0);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_spawn_background_exit_early(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
test_util_spawn_background_partial_read_impl(1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_spawn_background_waitpid_notify(void *arg)
|
||||
{
|
||||
int retval, exit_code;
|
||||
process_handle_t *process_handle=NULL;
|
||||
int status;
|
||||
int ms_timer;
|
||||
|
||||
const char *argv[] = {TEST_CHILD, "--fast", NULL};
|
||||
|
||||
(void) arg;
|
||||
|
||||
#ifdef _WIN32
|
||||
status = tor_spawn_background(NULL, argv, NULL, &process_handle);
|
||||
#else
|
||||
status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
|
||||
#endif
|
||||
|
||||
tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
|
||||
tt_ptr_op(process_handle, OP_NE, NULL);
|
||||
|
||||
/* We're not going to look at the stdout/stderr output this time. Instead,
|
||||
* we're testing whether notify_pending_waitpid_calbacks() can report the
|
||||
* process exit (on unix) and/or whether tor_get_exit_code() can notice it
|
||||
* (on windows) */
|
||||
|
||||
#ifndef _WIN32
|
||||
ms_timer = 30*1000;
|
||||
tt_ptr_op(process_handle->waitpid_cb, OP_NE, NULL);
|
||||
while (process_handle->waitpid_cb && ms_timer > 0) {
|
||||
tor_sleep_msec(100);
|
||||
ms_timer -= 100;
|
||||
notify_pending_waitpid_callbacks();
|
||||
}
|
||||
tt_int_op(ms_timer, OP_GT, 0);
|
||||
tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
|
||||
#endif
|
||||
|
||||
ms_timer = 30*1000;
|
||||
while (((retval = tor_get_exit_code(process_handle, 0, &exit_code))
|
||||
== PROCESS_EXIT_RUNNING) && ms_timer > 0) {
|
||||
tor_sleep_msec(100);
|
||||
ms_timer -= 100;
|
||||
}
|
||||
tt_int_op(ms_timer, OP_GT, 0);
|
||||
|
||||
tt_int_op(retval, OP_EQ, PROCESS_EXIT_EXITED);
|
||||
|
||||
done:
|
||||
tor_process_handle_destroy(process_handle, 1);
|
||||
}
|
||||
|
||||
#undef TEST_CHILD
|
||||
#undef EOL
|
||||
|
||||
#undef MATCH_PROCESS_STATUS
|
||||
|
||||
#ifndef _WIN32
|
||||
#undef PROCESS_STATUS_RUNNING_OR_NOTRUNNING
|
||||
#undef IS_RUNNING_OR_NOTRUNNING
|
||||
#endif
|
||||
|
||||
#define UTIL_TEST(name, flags) \
|
||||
{ #name, test_util_ ## name, flags, NULL, NULL }
|
||||
|
||||
struct testcase_t slow_util_tests[] = {
|
||||
UTIL_TEST(spawn_background_ok, 0),
|
||||
UTIL_TEST(spawn_background_fail, 0),
|
||||
UTIL_TEST(spawn_background_partial_read, 0),
|
||||
UTIL_TEST(spawn_background_exit_early, 0),
|
||||
UTIL_TEST(spawn_background_waitpid_notify, 0),
|
||||
END_OF_TESTCASES
|
||||
};
|
298
src/test/testing_common.c
Normal file
298
src/test/testing_common.c
Normal file
@ -0,0 +1,298 @@
|
||||
/* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2015, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/* Ordinarily defined in tor_main.c; this bit is just here to provide one
|
||||
* since we're not linking to tor_main.c */
|
||||
const char tor_git_revision[] = "";
|
||||
|
||||
/**
|
||||
* \file test_common.c
|
||||
* \brief Common pieces to implement unit tests.
|
||||
**/
|
||||
|
||||
#include "orconfig.h"
|
||||
#include "or.h"
|
||||
#include "config.h"
|
||||
#include "rephist.h"
|
||||
#include "backtrace.h"
|
||||
#include "test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
/* For mkdir() */
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#include "or.h"
|
||||
|
||||
#ifdef USE_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include "main.h"
|
||||
#endif
|
||||
|
||||
/** Temporary directory (set up by setup_directory) under which we store all
|
||||
* our files during testing. */
|
||||
static char temp_dir[256];
|
||||
#ifdef _WIN32
|
||||
#define pid_t int
|
||||
#endif
|
||||
static pid_t temp_dir_setup_in_pid = 0;
|
||||
|
||||
/** Select and create the temporary directory we'll use to run our unit tests.
|
||||
* Store it in <b>temp_dir</b>. Exit immediately if we can't create it.
|
||||
* idempotent. */
|
||||
static void
|
||||
setup_directory(void)
|
||||
{
|
||||
static int is_setup = 0;
|
||||
int r;
|
||||
char rnd[256], rnd32[256];
|
||||
if (is_setup) return;
|
||||
|
||||
/* Due to base32 limitation needs to be a multiple of 5. */
|
||||
#define RAND_PATH_BYTES 5
|
||||
crypto_rand(rnd, RAND_PATH_BYTES);
|
||||
base32_encode(rnd32, sizeof(rnd32), rnd, RAND_PATH_BYTES);
|
||||
|
||||
#ifdef _WIN32
|
||||
{
|
||||
char buf[MAX_PATH];
|
||||
const char *tmp = buf;
|
||||
const char *extra_backslash = "";
|
||||
/* If this fails, we're probably screwed anyway */
|
||||
if (!GetTempPathA(sizeof(buf),buf))
|
||||
tmp = "c:\\windows\\temp\\";
|
||||
if (strcmpend(tmp, "\\")) {
|
||||
/* According to MSDN, it should be impossible for GetTempPath to give us
|
||||
* an answer that doesn't end with \. But let's make sure. */
|
||||
extra_backslash = "\\";
|
||||
}
|
||||
tor_snprintf(temp_dir, sizeof(temp_dir),
|
||||
"%s%stor_test_%d_%s", tmp, extra_backslash,
|
||||
(int)getpid(), rnd32);
|
||||
r = mkdir(temp_dir);
|
||||
}
|
||||
#else
|
||||
tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s",
|
||||
(int) getpid(), rnd32);
|
||||
r = mkdir(temp_dir, 0700);
|
||||
if (!r) {
|
||||
/* undo sticky bit so tests don't get confused. */
|
||||
r = chown(temp_dir, getuid(), getgid());
|
||||
}
|
||||
#endif
|
||||
if (r) {
|
||||
fprintf(stderr, "Can't create directory %s:", temp_dir);
|
||||
perror("");
|
||||
exit(1);
|
||||
}
|
||||
is_setup = 1;
|
||||
temp_dir_setup_in_pid = getpid();
|
||||
}
|
||||
|
||||
/** Return a filename relative to our testing temporary directory */
|
||||
const char *
|
||||
get_fname(const char *name)
|
||||
{
|
||||
static char buf[1024];
|
||||
setup_directory();
|
||||
if (!name)
|
||||
return temp_dir;
|
||||
tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Remove a directory and all of its subdirectories */
|
||||
static void
|
||||
rm_rf(const char *dir)
|
||||
{
|
||||
struct stat st;
|
||||
smartlist_t *elements;
|
||||
|
||||
elements = tor_listdir(dir);
|
||||
if (elements) {
|
||||
SMARTLIST_FOREACH_BEGIN(elements, const char *, cp) {
|
||||
char *tmp = NULL;
|
||||
tor_asprintf(&tmp, "%s"PATH_SEPARATOR"%s", dir, cp);
|
||||
if (0 == stat(tmp,&st) && (st.st_mode & S_IFDIR)) {
|
||||
rm_rf(tmp);
|
||||
} else {
|
||||
if (unlink(tmp)) {
|
||||
fprintf(stderr, "Error removing %s: %s\n", tmp, strerror(errno));
|
||||
}
|
||||
}
|
||||
tor_free(tmp);
|
||||
} SMARTLIST_FOREACH_END(cp);
|
||||
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
|
||||
smartlist_free(elements);
|
||||
}
|
||||
if (rmdir(dir))
|
||||
fprintf(stderr, "Error removing directory %s: %s\n", dir, strerror(errno));
|
||||
}
|
||||
|
||||
/** Remove all files stored under the temporary directory, and the directory
|
||||
* itself. Called by atexit(). */
|
||||
static void
|
||||
remove_directory(void)
|
||||
{
|
||||
if (getpid() != temp_dir_setup_in_pid) {
|
||||
/* Only clean out the tempdir when the main process is exiting. */
|
||||
return;
|
||||
}
|
||||
|
||||
rm_rf(temp_dir);
|
||||
}
|
||||
|
||||
/** Define this if unit tests spend too much time generating public keys*/
|
||||
#undef CACHE_GENERATED_KEYS
|
||||
|
||||
static crypto_pk_t *pregen_keys[5] = {NULL, NULL, NULL, NULL, NULL};
|
||||
#define N_PREGEN_KEYS ARRAY_LENGTH(pregen_keys)
|
||||
|
||||
/** Generate and return a new keypair for use in unit tests. If we're using
|
||||
* the key cache optimization, we might reuse keys: we only guarantee that
|
||||
* keys made with distinct values for <b>idx</b> are different. The value of
|
||||
* <b>idx</b> must be at least 0, and less than N_PREGEN_KEYS. */
|
||||
crypto_pk_t *
|
||||
pk_generate(int idx)
|
||||
{
|
||||
#ifdef CACHE_GENERATED_KEYS
|
||||
tor_assert(idx < N_PREGEN_KEYS);
|
||||
if (! pregen_keys[idx]) {
|
||||
pregen_keys[idx] = crypto_pk_new();
|
||||
tor_assert(!crypto_pk_generate_key(pregen_keys[idx]));
|
||||
}
|
||||
return crypto_pk_dup_key(pregen_keys[idx]);
|
||||
#else
|
||||
crypto_pk_t *result;
|
||||
(void) idx;
|
||||
result = crypto_pk_new();
|
||||
tor_assert(!crypto_pk_generate_key(result));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Free all storage used for the cached key optimization. */
|
||||
static void
|
||||
free_pregenerated_keys(void)
|
||||
{
|
||||
unsigned idx;
|
||||
for (idx = 0; idx < N_PREGEN_KEYS; ++idx) {
|
||||
if (pregen_keys[idx]) {
|
||||
crypto_pk_free(pregen_keys[idx]);
|
||||
pregen_keys[idx] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
passthrough_test_setup(const struct testcase_t *testcase)
|
||||
{
|
||||
return testcase->setup_data;
|
||||
}
|
||||
static int
|
||||
passthrough_test_cleanup(const struct testcase_t *testcase, void *ptr)
|
||||
{
|
||||
(void)testcase;
|
||||
(void)ptr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct testcase_setup_t passthrough_setup = {
|
||||
passthrough_test_setup, passthrough_test_cleanup
|
||||
};
|
||||
|
||||
extern struct testgroup_t testgroups[];
|
||||
|
||||
/** Main entry point for unit test code: parse the command line, and run
|
||||
* some unit tests. */
|
||||
int
|
||||
main(int c, const char **v)
|
||||
{
|
||||
or_options_t *options;
|
||||
char *errmsg = NULL;
|
||||
int i, i_out;
|
||||
int loglevel = LOG_ERR;
|
||||
int accel_crypto = 0;
|
||||
|
||||
#ifdef USE_DMALLOC
|
||||
{
|
||||
int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_);
|
||||
tor_assert(r);
|
||||
}
|
||||
#endif
|
||||
|
||||
update_approx_time(time(NULL));
|
||||
options = options_new();
|
||||
tor_threads_init();
|
||||
init_logging(1);
|
||||
configure_backtrace_handler(get_version());
|
||||
|
||||
for (i_out = i = 1; i < c; ++i) {
|
||||
if (!strcmp(v[i], "--warn")) {
|
||||
loglevel = LOG_WARN;
|
||||
} else if (!strcmp(v[i], "--notice")) {
|
||||
loglevel = LOG_NOTICE;
|
||||
} else if (!strcmp(v[i], "--info")) {
|
||||
loglevel = LOG_INFO;
|
||||
} else if (!strcmp(v[i], "--debug")) {
|
||||
loglevel = LOG_DEBUG;
|
||||
} else if (!strcmp(v[i], "--accel")) {
|
||||
accel_crypto = 1;
|
||||
} else {
|
||||
v[i_out++] = v[i];
|
||||
}
|
||||
}
|
||||
c = i_out;
|
||||
|
||||
{
|
||||
log_severity_list_t s;
|
||||
memset(&s, 0, sizeof(s));
|
||||
set_log_severity_config(loglevel, LOG_ERR, &s);
|
||||
add_stream_log(&s, "", fileno(stdout));
|
||||
}
|
||||
|
||||
options->command = CMD_RUN_UNITTESTS;
|
||||
if (crypto_global_init(accel_crypto, NULL, NULL)) {
|
||||
printf("Can't initialize crypto subsystem; exiting.\n");
|
||||
return 1;
|
||||
}
|
||||
crypto_set_tls_dh_prime(NULL);
|
||||
crypto_seed_rng(1);
|
||||
rep_hist_init();
|
||||
network_init();
|
||||
setup_directory();
|
||||
options_init(options);
|
||||
options->DataDirectory = tor_strdup(temp_dir);
|
||||
options->EntryStatistics = 1;
|
||||
if (set_options(options, &errmsg) < 0) {
|
||||
printf("Failed to set initial options: %s\n", errmsg);
|
||||
tor_free(errmsg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
atexit(remove_directory);
|
||||
|
||||
int have_failed = (tinytest_main(c, v, testgroups) != 0);
|
||||
|
||||
free_pregenerated_keys();
|
||||
#ifdef USE_DMALLOC
|
||||
tor_free_all(0);
|
||||
dmalloc_log_unfreed();
|
||||
#endif
|
||||
|
||||
if (have_failed)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user