Cover channel crypto failures and require owner close after failed rollback
This commit is contained in:
1 parent
01c2ebd7d8
commit
f1cada4c02
2 files changed
+82
-2
No files matched your search
+3
-1
@@ -316,7 +316,9 @@ test_chat_channel_SOURCES = test_chat_channel.c
|
||||
test_chat_channel_LDADD = $(LIBUTUN) $(COMMON_LIBS)
|
||||
test_chat_channel_LDFLAGS = -Wl,--wrap=EVP_DigestSign -Wl,--wrap=sqlite3_exec -Wl,--wrap=sqlite3_prepare_v2 \
|
||||
-Wl,--wrap=sqlite3_bind_blob -Wl,--wrap=sqlite3_bind_text -Wl,--wrap=sqlite3_bind_int64 -Wl,--wrap=sqlite3_bind_null \
|
||||
-Wl,--wrap=sqlite3_step -Wl,--wrap=sqlite3_changes -Wl,--wrap=sqlite3_finalize -Wl,--wrap=u_malloc_impl
|
||||
-Wl,--wrap=sqlite3_step -Wl,--wrap=sqlite3_changes -Wl,--wrap=sqlite3_finalize -Wl,--wrap=u_malloc_impl \
|
||||
-Wl,--wrap=EVP_PKEY_CTX_new_id -Wl,--wrap=EVP_PKEY_keygen_init -Wl,--wrap=EVP_PKEY_keygen \
|
||||
-Wl,--wrap=EVP_PKEY_get_raw_public_key -Wl,--wrap=EVP_PKEY_get_raw_private_key -Wl,--wrap=RAND_bytes
|
||||
else
|
||||
test_socket_monitor_LDFLAGS += -Wl,--wrap=read
|
||||
endif
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <openssl/rand.h>
|
||||
#include "../src/utun_instance.h"
|
||||
#include "../src/chat/chat_core.h"
|
||||
#include "../src/chat/chat_core_priv.h"
|
||||
@@ -22,6 +23,55 @@ enum sql_fault { Q_NONE, Q_PREPARE, Q_BIND, Q_STEP, Q_CHANGES, Q_FINALIZE, Q_EXE
|
||||
static enum sql_fault sql_fault;
|
||||
static const char *sql_prefix, *sql_suffix;
|
||||
static int sql_bind, allocations_until_failure, failed_rollback, last_target;
|
||||
enum crypto_fault { K_NONE, K_CONTEXT, K_INITIALIZE, K_GENERATE, K_PUBLIC, K_PUBLIC_SIZE, K_PRIVATE, K_PRIVATE_SIZE,
|
||||
K_RANDOM, K_DESCRIPTION, K_INVITATION, K_PROFILE };
|
||||
static enum crypto_fault crypto_fault;
|
||||
static int crypto_type;
|
||||
static EVP_PKEY_CTX* crypto_context;
|
||||
static EVP_PKEY* crypto_key;
|
||||
|
||||
static int take_crypto(enum crypto_fault point) {
|
||||
if (crypto_fault != point) return 0;
|
||||
crypto_fault = K_NONE; DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "channel test: crypto fault type=%d stage=%d", crypto_type, point); return 1;
|
||||
}
|
||||
extern EVP_PKEY_CTX* __real_EVP_PKEY_CTX_new_id(int, ENGINE*);
|
||||
EVP_PKEY_CTX* __wrap_EVP_PKEY_CTX_new_id(int type, ENGINE* engine) {
|
||||
if (type == crypto_type && take_crypto(K_CONTEXT)) return NULL;
|
||||
EVP_PKEY_CTX* ctx = __real_EVP_PKEY_CTX_new_id(type, engine);
|
||||
if (type == crypto_type && crypto_fault != K_NONE) crypto_context = ctx;
|
||||
return ctx;
|
||||
}
|
||||
extern int __real_EVP_PKEY_keygen_init(EVP_PKEY_CTX*);
|
||||
int __wrap_EVP_PKEY_keygen_init(EVP_PKEY_CTX* ctx) {
|
||||
if (ctx == crypto_context && take_crypto(K_INITIALIZE)) return 0;
|
||||
return __real_EVP_PKEY_keygen_init(ctx);
|
||||
}
|
||||
extern int __real_EVP_PKEY_keygen(EVP_PKEY_CTX*, EVP_PKEY**);
|
||||
int __wrap_EVP_PKEY_keygen(EVP_PKEY_CTX* ctx, EVP_PKEY** key) {
|
||||
if (ctx == crypto_context && take_crypto(K_GENERATE)) return 0;
|
||||
int rc = __real_EVP_PKEY_keygen(ctx, key);
|
||||
if (ctx == crypto_context && rc == 1 && crypto_fault != K_NONE) crypto_key = *key;
|
||||
return rc;
|
||||
}
|
||||
extern int __real_EVP_PKEY_get_raw_public_key(const EVP_PKEY*, unsigned char*, size_t*);
|
||||
int __wrap_EVP_PKEY_get_raw_public_key(const EVP_PKEY* key, unsigned char* data, size_t* length) {
|
||||
if (key == crypto_key && take_crypto(K_PUBLIC)) return 0;
|
||||
int rc = __real_EVP_PKEY_get_raw_public_key(key, data, length);
|
||||
if (key == crypto_key && take_crypto(K_PUBLIC_SIZE)) *length = 31;
|
||||
return rc;
|
||||
}
|
||||
extern int __real_EVP_PKEY_get_raw_private_key(const EVP_PKEY*, unsigned char*, size_t*);
|
||||
int __wrap_EVP_PKEY_get_raw_private_key(const EVP_PKEY* key, unsigned char* data, size_t* length) {
|
||||
if (key == crypto_key && take_crypto(K_PRIVATE)) return 0;
|
||||
int rc = __real_EVP_PKEY_get_raw_private_key(key, data, length);
|
||||
if (key == crypto_key && take_crypto(K_PRIVATE_SIZE)) *length = 31;
|
||||
return rc;
|
||||
}
|
||||
extern int __real_RAND_bytes(unsigned char*, int);
|
||||
int __wrap_RAND_bytes(unsigned char* data, int length) {
|
||||
if (length == 8 && take_crypto(K_RANDOM)) return 0;
|
||||
return __real_RAND_bytes(data, length);
|
||||
}
|
||||
|
||||
static int sql_target(const char* sql) {
|
||||
return sql_prefix && !strncmp(sql, sql_prefix, strlen(sql_prefix)) && (!sql_suffix || strstr(sql, sql_suffix));
|
||||
@@ -125,6 +175,12 @@ int __wrap_EVP_DigestSign(EVP_MD_CTX* ctx, unsigned char* signature, size_t* len
|
||||
if (fail_sign_number && active && size == 112 && !memcmp(data + 72, active->my_keys.public_key, 32) && !--fail_sign_number) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "channel test: injected signature failure bytes=%zu", size); return 0;
|
||||
}
|
||||
if (active && size == MEMBER_SIGN_MSG_SIZE && !memcmp(data, active->my_keys.public_key, 32) &&
|
||||
!memcmp(data + 32, MEMBER_SIGN_SUFFIX, MEMBER_SIGN_SUFFIX_LEN) && take_crypto(K_INVITATION)) return 0;
|
||||
if (size >= 80 && !memcmp(data + 72, "{\"name\":", 8) && take_crypto(K_PROFILE)) return 0;
|
||||
const uint8_t* zero = memchr(data, 0, size);
|
||||
if (zero && zero - data < 20 && (size_t)(zero - data + 1) + sizeof("crypto refusal") <= size &&
|
||||
!memcmp(zero + 1, "crypto refusal", sizeof("crypto refusal")) && take_crypto(K_DESCRIPTION)) return 0;
|
||||
return __real_EVP_DigestSign(ctx, signature, length, data, size);
|
||||
}
|
||||
|
||||
@@ -168,7 +224,7 @@ static void refused(struct UTUN_INSTANCE* inst, const char* name) {
|
||||
uint64_t created = chat_core_create_channel_auto(inst, name);
|
||||
if (created || sql_fault != Q_NONE) DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC,
|
||||
"channel test: expected refusal name=%s ID=%llu pending_fault=%d", name, (unsigned long long)created, sql_fault);
|
||||
assert(!created && sql_fault == Q_NONE && sqlite3_get_autocommit(inst->topo_sqlite_db));
|
||||
assert(!created && sql_fault == Q_NONE && crypto_fault == K_NONE && !fail_sign_number && sqlite3_get_autocommit(inst->topo_sqlite_db));
|
||||
snapshot(reader, after); assert(!memcmp(before, after, 32) && channels() == count && channel_events == events);
|
||||
assert(queue_entry_count(inst->topo_groups->group_list) == groups);
|
||||
}
|
||||
@@ -180,6 +236,22 @@ static void retry(struct UTUN_INSTANCE* inst) {
|
||||
|
||||
static int veto(void* arg) { (*(unsigned*)arg)++; return 1; }
|
||||
|
||||
static void crypto_regressions(struct UTUN_INSTANCE* inst) {
|
||||
const int types[] = {EVP_PKEY_X25519, EVP_PKEY_ED25519}; unsigned cases = 0;
|
||||
for (unsigned type = 0; type < 2; type++) {
|
||||
for (enum crypto_fault point = K_CONTEXT; point <= K_PRIVATE_SIZE; point++) {
|
||||
crypto_type = types[type]; crypto_context = NULL; crypto_key = NULL; crypto_fault = point;
|
||||
refused(inst, "crypto refusal"); retry(inst); cases++;
|
||||
}
|
||||
}
|
||||
crypto_type = 0; crypto_context = NULL; crypto_key = NULL;
|
||||
for (enum crypto_fault point = K_RANDOM; point <= K_PROFILE; point++) {
|
||||
crypto_fault = point; refused(inst, "crypto refusal"); retry(inst); cases++;
|
||||
}
|
||||
fail_sign_number = 1; refused(inst, "crypto refusal"); retry(inst); cases++;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS channel crypto:%u faults/retries; key generation/extraction sizes,RNG,description,JOIN,invitation,profile", cases);
|
||||
}
|
||||
|
||||
static void sql_regressions(struct UTUN_INSTANCE* inst) {
|
||||
const char* queries[] = {"SELECT 1 FROM channels WHERE channel_id=?", "INSERT INTO channels(channel_id, name, owner_node_id,",
|
||||
"INSERT INTO \"peers_"};
|
||||
@@ -248,8 +320,14 @@ int main(void) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "channel test: profile bind failure ID=%llu channels=%d events=%u",
|
||||
(unsigned long long)created, channels(), channel_events);
|
||||
assert(!fail_profile_binding && !created && channels() == 1 && channel_events == 1);
|
||||
crypto_regressions(inst);
|
||||
sql_regressions(inst);
|
||||
int committed = channels();
|
||||
uint8_t before[32], after[32]; snapshot(reader, before); unsigned events = channel_events;
|
||||
sql_prefix = "COMMIT /* channel creation */"; sql_suffix = NULL; sql_fault = Q_EXEC; failed_rollback = 1; inst->running = 1;
|
||||
assert(!chat_core_create_channel_auto(inst, "failed rollback") && sql_fault == Q_NONE && !failed_rollback);
|
||||
assert(!inst->running && !sqlite3_get_autocommit(inst->topo_sqlite_db) && channel_events == events);
|
||||
snapshot(reader, after); assert(!memcmp(before, after, 32));
|
||||
utun_instance_destroy(inst); active = NULL; assert(channels() == committed && sqlite3_close(reader) == SQLITE_OK); reader = NULL;
|
||||
uasync_poll(ua, 0); uasync_destroy(ua, 0); assert(u_get_allocated_count() == baseline);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS channel creation: failed root signature leaves no channel/event; retry publishes valid membership after commit");
|
||||
|
||||
Reference in new issue
Block a user