first
This commit is contained in:
158
tests/test_dispatcher.c
Normal file
158
tests/test_dispatcher.c
Normal file
@@ -0,0 +1,158 @@
|
||||
#include "../src/dispatcher.h"
|
||||
|
||||
#include "../src/enforcement.h"
|
||||
#include "../src/role_table.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int g_passes = 0;
|
||||
static int g_total = 0;
|
||||
|
||||
static void check_condition(const char *name, int condition) {
|
||||
g_total++;
|
||||
if (condition) {
|
||||
printf("PASS: %s\n", name);
|
||||
g_passes++;
|
||||
} else {
|
||||
printf("FAIL: %s\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
static int response_has(const char *response, const char *needle) {
|
||||
return (response != NULL && needle != NULL && strstr(response, needle) != NULL);
|
||||
}
|
||||
|
||||
static role_entry_t make_nostr_entry(const char *name, int idx) {
|
||||
role_entry_t e;
|
||||
|
||||
memset(&e, 0, sizeof(e));
|
||||
strncpy(e.name, name, sizeof(e.name) - 1);
|
||||
strncpy(e.purpose_str, "nostr", sizeof(e.purpose_str) - 1);
|
||||
strncpy(e.curve_str, "secp256k1", sizeof(e.curve_str) - 1);
|
||||
e.purpose = role_purpose_from_str(e.purpose_str);
|
||||
e.curve = role_curve_from_str(e.curve_str);
|
||||
e.selector_type = SELECTOR_NOSTR_INDEX;
|
||||
e.nostr_index = idx;
|
||||
e.derived = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static role_entry_t make_path_entry(const char *name, const char *purpose, const char *curve, const char *path) {
|
||||
role_entry_t e;
|
||||
|
||||
memset(&e, 0, sizeof(e));
|
||||
strncpy(e.name, name, sizeof(e.name) - 1);
|
||||
strncpy(e.purpose_str, purpose, sizeof(e.purpose_str) - 1);
|
||||
strncpy(e.curve_str, curve, sizeof(e.curve_str) - 1);
|
||||
strncpy(e.role_path, path, sizeof(e.role_path) - 1);
|
||||
e.purpose = role_purpose_from_str(e.purpose_str);
|
||||
e.curve = role_curve_from_str(e.curve_str);
|
||||
e.selector_type = SELECTOR_ROLE_PATH;
|
||||
e.nostr_index = -1;
|
||||
e.derived = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
role_table_t table;
|
||||
role_entry_t main_role;
|
||||
role_entry_t ssh_role;
|
||||
mnemonic_state_t mnemonic;
|
||||
dispatcher_ctx_t dispatcher;
|
||||
const char *valid_12 = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
||||
char *resp;
|
||||
|
||||
role_table_init(&table);
|
||||
|
||||
main_role = make_nostr_entry("main", 0);
|
||||
ssh_role = make_path_entry("ssh_key", "ssh", "ed25519", "m/44'/822'/0'/0/0");
|
||||
|
||||
role_table_add(&table, &main_role);
|
||||
role_table_add(&table, &ssh_role);
|
||||
|
||||
mnemonic_init(&mnemonic);
|
||||
mnemonic_load(&mnemonic, valid_12);
|
||||
|
||||
dispatcher_init(&dispatcher, &table, &mnemonic);
|
||||
|
||||
/* 1. Valid get_public_key no selector -> default main, not_yet_derived */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"1\",\"method\":\"get_public_key\",\"params\":[\"\"]}");
|
||||
check_condition("get_public_key default role returns not_yet_derived",
|
||||
response_has(resp, "\"id\":\"1\"") && response_has(resp, "\"result\":\"not_yet_derived\""));
|
||||
free(resp);
|
||||
|
||||
/* 2. sign_event with role main */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"2\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"main\"}]}");
|
||||
check_condition("sign_event with role=main returns stub",
|
||||
response_has(resp, "\"id\":\"2\"") && response_has(resp, "\"result\":\"stub:sign_event_ok\""));
|
||||
free(resp);
|
||||
|
||||
/* 3. sign_event with nostr_index 0 */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"3\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"nostr_index\":0}]}");
|
||||
check_condition("sign_event with nostr_index=0 returns stub",
|
||||
response_has(resp, "\"id\":\"3\"") && response_has(resp, "\"result\":\"stub:sign_event_ok\""));
|
||||
free(resp);
|
||||
|
||||
/* 4. ambiguous selector role + nostr_index */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"4\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"main\",\"nostr_index\":0}]}");
|
||||
check_condition("ambiguous selector returns 1001",
|
||||
response_has(resp, "\"id\":\"4\"") && response_has(resp, "\"code\":1001"));
|
||||
free(resp);
|
||||
|
||||
/* 5. role not found */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"5\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"nonexistent\"}]}");
|
||||
check_condition("role not found returns 1002",
|
||||
response_has(resp, "\"id\":\"5\"") && response_has(resp, "\"code\":1002"));
|
||||
free(resp);
|
||||
|
||||
/* 6. purpose mismatch: sign_event against ssh role */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"6\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"ssh_key\"}]}");
|
||||
check_condition("purpose mismatch returns 1004",
|
||||
response_has(resp, "\"id\":\"6\"") && response_has(resp, "\"code\":1004"));
|
||||
free(resp);
|
||||
|
||||
/* 7. invalid JSON */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"7\",\"method\":\"sign_event\",\"params\":[\"{}\"]");
|
||||
check_condition("invalid JSON returns -32700",
|
||||
response_has(resp, "\"code\":-32700"));
|
||||
free(resp);
|
||||
|
||||
/* 8. missing method */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"8\",\"params\":[\"{}\"]}");
|
||||
check_condition("missing method returns -32600",
|
||||
response_has(resp, "\"id\":\"8\"") && response_has(resp, "\"code\":-32600"));
|
||||
free(resp);
|
||||
|
||||
/* 9. mnemonic not loaded */
|
||||
mnemonic_unload(&mnemonic);
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"9\",\"method\":\"get_public_key\",\"params\":[\"\"]}");
|
||||
check_condition("mnemonic not loaded returns 1006",
|
||||
response_has(resp, "\"id\":\"9\"") && response_has(resp, "\"code\":1006"));
|
||||
free(resp);
|
||||
|
||||
mnemonic_load(&mnemonic, valid_12);
|
||||
|
||||
/* 10. unknown verb via enforcement */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"10\",\"method\":\"foo_bar\",\"params\":[\"\"]}");
|
||||
check_condition("unknown verb returns -32601",
|
||||
response_has(resp, "\"id\":\"10\"") && response_has(resp, "\"code\":-32601"));
|
||||
free(resp);
|
||||
|
||||
printf("%d/10 tests passed\n", g_passes);
|
||||
|
||||
return (g_passes == 10 && g_total == 10) ? 0 : 1;
|
||||
}
|
||||
92
tests/test_enforcement.c
Normal file
92
tests/test_enforcement.c
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "../src/enforcement.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int g_passes = 0;
|
||||
static int g_total = 0;
|
||||
|
||||
static void check_condition(const char *name, int condition) {
|
||||
g_total++;
|
||||
if (condition) {
|
||||
printf("PASS: %s\n", name);
|
||||
g_passes++;
|
||||
} else {
|
||||
printf("FAIL: %s\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
static role_entry_t make_role(const char *purpose, const char *curve) {
|
||||
role_entry_t role;
|
||||
|
||||
memset(&role, 0, sizeof(role));
|
||||
strncpy(role.name, "test_role", sizeof(role.name) - 1);
|
||||
strncpy(role.purpose_str, purpose, sizeof(role.purpose_str) - 1);
|
||||
strncpy(role.curve_str, curve, sizeof(role.curve_str) - 1);
|
||||
role.purpose = role_purpose_from_str(role.purpose_str);
|
||||
role.curve = role_curve_from_str(role.curve_str);
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
role_entry_t nostr_secp = make_role("nostr", "secp256k1");
|
||||
role_entry_t bitcoin_secp = make_role("bitcoin", "secp256k1");
|
||||
role_entry_t nostr_ed = make_role("nostr", "ed25519");
|
||||
role_entry_t ssh_ed = make_role("ssh", "ed25519");
|
||||
role_entry_t age_x = make_role("age", "x25519");
|
||||
|
||||
check_condition(
|
||||
"sign_event + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_SIGN_EVENT, &nostr_secp) == ENFORCE_OK
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"get_public_key + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_GET_PUBLIC_KEY, &nostr_secp) == ENFORCE_OK
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"nip44_encrypt + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_NIP44_ENCRYPT, &nostr_secp) == ENFORCE_OK
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"nip44_decrypt + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_NIP44_DECRYPT, &nostr_secp) == ENFORCE_OK
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"nip04_encrypt + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_NIP04_ENCRYPT, &nostr_secp) == ENFORCE_OK
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"sign_event + bitcoin/secp256k1 -> ENFORCE_ERR_PURPOSE",
|
||||
enforce_verb_role(VERB_SIGN_EVENT, &bitcoin_secp) == ENFORCE_ERR_PURPOSE
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"sign_event + nostr/ed25519 -> ENFORCE_ERR_CURVE",
|
||||
enforce_verb_role(VERB_SIGN_EVENT, &nostr_ed) == ENFORCE_ERR_CURVE
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"sign_event + ssh/ed25519 -> ENFORCE_ERR_PURPOSE",
|
||||
enforce_verb_role(VERB_SIGN_EVENT, &ssh_ed) == ENFORCE_ERR_PURPOSE
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"unknown_verb + nostr/secp256k1 -> ENFORCE_ERR_UNKNOWN_VERB",
|
||||
enforce_verb_role("unknown_verb", &nostr_secp) == ENFORCE_ERR_UNKNOWN_VERB
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"nip44_encrypt + age/x25519 -> ENFORCE_ERR_PURPOSE",
|
||||
enforce_verb_role(VERB_NIP44_ENCRYPT, &age_x) == ENFORCE_ERR_PURPOSE
|
||||
);
|
||||
|
||||
printf("%d/10 tests passed\n", g_passes);
|
||||
|
||||
return (g_passes == 10 && g_total == 10) ? 0 : 1;
|
||||
}
|
||||
256
tests/test_integration.c
Normal file
256
tests/test_integration.c
Normal file
@@ -0,0 +1,256 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define SOCKET_NAME "nsigner"
|
||||
#define MAX_MSG_SIZE 65536
|
||||
|
||||
static int g_failures = 0;
|
||||
|
||||
static void check_condition(const char *name, int condition) {
|
||||
if (condition) {
|
||||
printf("PASS: %s\n", name);
|
||||
} else {
|
||||
printf("FAIL: %s\n", name);
|
||||
g_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static int sleep_ms(int ms) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (long)(ms % 1000) * 1000000L;
|
||||
return nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
static int read_full(int fd, void *buf, size_t len) {
|
||||
unsigned char *p = (unsigned char *)buf;
|
||||
size_t off = 0;
|
||||
|
||||
while (off < len) {
|
||||
ssize_t n = read(fd, p + off, len - off);
|
||||
if (n == 0) {
|
||||
return -1;
|
||||
}
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
off += (size_t)n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_full(int fd, const void *buf, size_t len) {
|
||||
const unsigned char *p = (const unsigned char *)buf;
|
||||
size_t off = 0;
|
||||
|
||||
while (off < len) {
|
||||
ssize_t n = write(fd, p + off, len - off);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
off += (size_t)n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connect_socket_retry(const char *name, int timeout_ms) {
|
||||
int elapsed = 0;
|
||||
|
||||
while (elapsed < timeout_ms) {
|
||||
int fd;
|
||||
struct sockaddr_un addr;
|
||||
socklen_t addr_len;
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
addr.sun_path[0] = '\0';
|
||||
strncpy(&addr.sun_path[1], name, sizeof(addr.sun_path) - 2);
|
||||
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
|
||||
addr_len = (socklen_t)(sizeof(sa_family_t) + 1 + strlen(name));
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&addr, addr_len) == 0) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
sleep_ms(100);
|
||||
elapsed += 100;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int send_framed(int fd, const char *payload) {
|
||||
uint32_t len = (uint32_t)strlen(payload);
|
||||
uint32_t be_len = htonl(len);
|
||||
|
||||
if (write_full(fd, &be_len, sizeof(be_len)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (write_full(fd, payload, len) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int recv_framed(int fd, char **out_payload) {
|
||||
uint32_t be_len;
|
||||
uint32_t len;
|
||||
char *payload;
|
||||
|
||||
if (out_payload == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out_payload = NULL;
|
||||
|
||||
if (read_full(fd, &be_len, sizeof(be_len)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = ntohl(be_len);
|
||||
if (len == 0 || len > MAX_MSG_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
payload = (char *)malloc((size_t)len + 1U);
|
||||
if (payload == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_full(fd, payload, len) != 0) {
|
||||
free(payload);
|
||||
return -1;
|
||||
}
|
||||
|
||||
payload[len] = '\0';
|
||||
*out_payload = payload;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int request_roundtrip(const char *req, char **resp) {
|
||||
int fd = connect_socket_retry(SOCKET_NAME, 5000);
|
||||
int rc;
|
||||
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = send_framed(fd, req);
|
||||
if (rc == 0) {
|
||||
rc = recv_framed(fd, resp);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int stdin_pipe[2];
|
||||
pid_t child;
|
||||
const char *mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\n\n";
|
||||
char *resp = NULL;
|
||||
int status;
|
||||
|
||||
if (pipe(stdin_pipe) != 0) {
|
||||
perror("pipe");
|
||||
return 1;
|
||||
}
|
||||
|
||||
child = fork();
|
||||
if (child < 0) {
|
||||
perror("fork");
|
||||
close(stdin_pipe[0]);
|
||||
close(stdin_pipe[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (child == 0) {
|
||||
int null_fd = open("/dev/null", O_WRONLY);
|
||||
if (null_fd >= 0) {
|
||||
dup2(null_fd, STDOUT_FILENO);
|
||||
dup2(null_fd, STDERR_FILENO);
|
||||
close(null_fd);
|
||||
}
|
||||
|
||||
dup2(stdin_pipe[0], STDIN_FILENO);
|
||||
close(stdin_pipe[0]);
|
||||
close(stdin_pipe[1]);
|
||||
|
||||
execl("./build/nsigner", "./build/nsigner", (char *)NULL);
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
close(stdin_pipe[0]);
|
||||
if (write_full(stdin_pipe[1], mnemonic, strlen(mnemonic)) == 0) {
|
||||
check_condition("feed mnemonic to child stdin", 1);
|
||||
} else {
|
||||
check_condition("feed mnemonic to child stdin", 0);
|
||||
}
|
||||
close(stdin_pipe[1]);
|
||||
|
||||
sleep_ms(800);
|
||||
|
||||
if (request_roundtrip("{\"id\":\"1\",\"method\":\"get_public_key\",\"params\":[\"\"]}", &resp) == 0) {
|
||||
check_condition("get_public_key response id", strstr(resp, "\"id\":\"1\"") != NULL);
|
||||
check_condition("get_public_key has result", strstr(resp, "\"result\":") != NULL);
|
||||
} else {
|
||||
check_condition("get_public_key request roundtrip", 0);
|
||||
}
|
||||
free(resp);
|
||||
resp = NULL;
|
||||
|
||||
if (request_roundtrip("{\"id\":\"2\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"main\"}]}", &resp) == 0) {
|
||||
check_condition("sign_event response id", strstr(resp, "\"id\":\"2\"") != NULL);
|
||||
check_condition("sign_event stub result", strstr(resp, "stub:sign_event_ok") != NULL);
|
||||
} else {
|
||||
check_condition("sign_event request roundtrip", 0);
|
||||
}
|
||||
free(resp);
|
||||
|
||||
if (kill(child, SIGTERM) == 0) {
|
||||
check_condition("send SIGTERM to child", 1);
|
||||
} else {
|
||||
check_condition("send SIGTERM to child", 0);
|
||||
}
|
||||
|
||||
if (waitpid(child, &status, 0) > 0) {
|
||||
check_condition("child exited", WIFEXITED(status) || WIFSIGNALED(status));
|
||||
} else {
|
||||
check_condition("child exited", 0);
|
||||
}
|
||||
|
||||
if (g_failures == 0) {
|
||||
printf("ALL TESTS PASSED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("TESTS FAILED: %d\n", g_failures);
|
||||
return 1;
|
||||
}
|
||||
83
tests/test_mnemonic.c
Normal file
83
tests/test_mnemonic.c
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "../src/mnemonic.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int g_failures = 0;
|
||||
|
||||
/* Print PASS/FAIL and track failures. */
|
||||
static void check_condition(const char *name, int condition) {
|
||||
if (condition) {
|
||||
printf("PASS: %s\n", name);
|
||||
} else {
|
||||
printf("FAIL: %s\n", name);
|
||||
g_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Build a deterministic 25-word invalid mnemonic in `out`. */
|
||||
static void build_25_word_phrase(char *out, size_t out_size) {
|
||||
size_t used = 0;
|
||||
int i;
|
||||
|
||||
if (out == NULL || out_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
out[0] = '\0';
|
||||
for (i = 0; i < 25; ++i) {
|
||||
const char *word = "abandon";
|
||||
int wrote;
|
||||
|
||||
wrote = snprintf(out + used, out_size - used, "%s%s", (i == 0) ? "" : " ", word);
|
||||
if (wrote < 0 || (size_t)wrote >= out_size - used) {
|
||||
/* Truncate safely and stop if buffer is insufficient. */
|
||||
out[out_size - 1] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
used += (size_t)wrote;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
mnemonic_state_t state;
|
||||
const char *valid_12 = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
||||
const char *invalid_11 = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
||||
char invalid_25[MNEMONIC_MAX_LEN];
|
||||
const char *loaded_phrase;
|
||||
int rc;
|
||||
|
||||
mnemonic_init(&state);
|
||||
check_condition("state initially unloaded", mnemonic_is_loaded(&state) == 0);
|
||||
|
||||
rc = mnemonic_load(&state, valid_12);
|
||||
check_condition("load valid 12-word mnemonic returns 0", rc == 0);
|
||||
check_condition("state loaded after valid load", mnemonic_is_loaded(&state) == 1);
|
||||
check_condition("word_count == 12", state.word_count == 12);
|
||||
|
||||
loaded_phrase = mnemonic_get_phrase(&state);
|
||||
check_condition("mnemonic_get_phrase non-NULL when loaded", loaded_phrase != NULL);
|
||||
check_condition("mnemonic_get_phrase matches input", loaded_phrase != NULL && strcmp(loaded_phrase, valid_12) == 0);
|
||||
|
||||
mnemonic_unload(&state);
|
||||
check_condition("state unloaded after mnemonic_unload", mnemonic_is_loaded(&state) == 0);
|
||||
check_condition("mnemonic_get_phrase NULL after unload", mnemonic_get_phrase(&state) == NULL);
|
||||
|
||||
rc = mnemonic_load(&state, invalid_11);
|
||||
check_condition("reject invalid 11-word mnemonic", rc == -1);
|
||||
check_condition("state remains unloaded after invalid 11-word load", mnemonic_is_loaded(&state) == 0);
|
||||
|
||||
build_25_word_phrase(invalid_25, sizeof(invalid_25));
|
||||
rc = mnemonic_load(&state, invalid_25);
|
||||
check_condition("reject invalid 25-word mnemonic", rc == -1);
|
||||
check_condition("state remains unloaded after invalid 25-word load", mnemonic_is_loaded(&state) == 0);
|
||||
|
||||
if (g_failures == 0) {
|
||||
printf("ALL TESTS PASSED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("TESTS FAILED: %d\n", g_failures);
|
||||
return 1;
|
||||
}
|
||||
127
tests/test_policy.c
Normal file
127
tests/test_policy.c
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "../src/policy.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int g_passes = 0;
|
||||
static int g_total = 0;
|
||||
|
||||
static void check_condition(const char *name, int condition) {
|
||||
g_total++;
|
||||
if (condition) {
|
||||
printf("PASS: %s\n", name);
|
||||
g_passes++;
|
||||
} else {
|
||||
printf("FAIL: %s\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_single_entry(policy_table_t *table,
|
||||
const char *caller,
|
||||
const char *verb,
|
||||
const char *role,
|
||||
const char *purpose,
|
||||
prompt_mode_t prompt) {
|
||||
policy_entry_t e;
|
||||
|
||||
memset(&e, 0, sizeof(e));
|
||||
strncpy(e.caller, caller, sizeof(e.caller) - 1);
|
||||
if (verb != NULL) {
|
||||
strncpy(e.verbs[0], verb, sizeof(e.verbs[0]) - 1);
|
||||
e.verb_count = 1;
|
||||
}
|
||||
if (role != NULL) {
|
||||
strncpy(e.roles[0], role, sizeof(e.roles[0]) - 1);
|
||||
e.role_count = 1;
|
||||
}
|
||||
if (purpose != NULL) {
|
||||
strncpy(e.purposes[0], purpose, sizeof(e.purposes[0]) - 1);
|
||||
e.purpose_count = 1;
|
||||
}
|
||||
e.prompt = prompt;
|
||||
|
||||
policy_table_add(table, &e);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
policy_table_t table;
|
||||
int rc;
|
||||
uid_t uid = getuid();
|
||||
char same_uid[64];
|
||||
char other_uid[64];
|
||||
|
||||
(void)snprintf(same_uid, sizeof(same_uid), "uid:%u", (unsigned int)uid);
|
||||
(void)snprintf(other_uid, sizeof(other_uid), "uid:%u", (unsigned int)(uid + 1U));
|
||||
|
||||
policy_init_default(&table, uid);
|
||||
rc = policy_check(&table, same_uid, "sign_event", "main", "nostr");
|
||||
check_condition("policy_init_default allows same uid", rc == POLICY_ALLOW);
|
||||
|
||||
rc = policy_check(&table, other_uid, "sign_event", "main", "nostr");
|
||||
check_condition("policy_init_default denies different uid", rc == POLICY_DENY);
|
||||
|
||||
/* Exact caller, verb, role, purpose + never => allow */
|
||||
policy_table_init(&table);
|
||||
add_single_entry(&table, "uid:1000", "sign_event", "main", "nostr", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:1000", "sign_event", "main", "nostr");
|
||||
check_condition("exact match returns POLICY_ALLOW", rc == POLICY_ALLOW);
|
||||
|
||||
/* Wildcard caller + deny => deny */
|
||||
policy_table_init(&table);
|
||||
add_single_entry(&table, "*", "sign_event", "main", "nostr", PROMPT_DENY);
|
||||
rc = policy_check(&table, "uid:2000", "sign_event", "main", "nostr");
|
||||
check_condition("wildcard caller with deny returns POLICY_DENY", rc == POLICY_DENY);
|
||||
|
||||
/* Caller no match => POLICY_NO_MATCH */
|
||||
policy_table_init(&table);
|
||||
add_single_entry(&table, "uid:1000", "sign_event", "main", "nostr", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:9999", "sign_event", "main", "nostr");
|
||||
check_condition("caller mismatch returns POLICY_NO_MATCH", rc == POLICY_NO_MATCH);
|
||||
|
||||
/* Verb not in list => no match */
|
||||
policy_table_init(&table);
|
||||
add_single_entry(&table, "uid:1000", "get_public_key", "main", "nostr", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:1000", "sign_event", "main", "nostr");
|
||||
check_condition("verb mismatch returns POLICY_NO_MATCH", rc == POLICY_NO_MATCH);
|
||||
|
||||
/* Role not in list => no match */
|
||||
policy_table_init(&table);
|
||||
add_single_entry(&table, "uid:1000", "sign_event", "throwaway", "nostr", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:1000", "sign_event", "main", "nostr");
|
||||
check_condition("role mismatch returns POLICY_NO_MATCH", rc == POLICY_NO_MATCH);
|
||||
|
||||
/* Purpose not in list => no match */
|
||||
policy_table_init(&table);
|
||||
add_single_entry(&table, "uid:1000", "sign_event", "main", "bitcoin", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:1000", "sign_event", "main", "nostr");
|
||||
check_condition("purpose mismatch returns POLICY_NO_MATCH", rc == POLICY_NO_MATCH);
|
||||
|
||||
/* Empty verbs list means all verbs */
|
||||
policy_table_init(&table);
|
||||
add_single_entry(&table, "uid:1000", NULL, "main", "nostr", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:1000", "nip44_encrypt", "main", "nostr");
|
||||
check_condition("empty verbs list matches any verb", rc == POLICY_ALLOW);
|
||||
|
||||
/* prompt=first_per_boot => POLICY_PROMPT */
|
||||
policy_table_init(&table);
|
||||
add_single_entry(&table, "uid:1000", "sign_event", "main", "nostr", PROMPT_FIRST_PER_BOOT);
|
||||
rc = policy_check(&table, "uid:1000", "sign_event", "main", "nostr");
|
||||
check_condition("first_per_boot returns POLICY_PROMPT", rc == POLICY_PROMPT);
|
||||
|
||||
/* prompt=every_request => POLICY_PROMPT */
|
||||
policy_table_init(&table);
|
||||
add_single_entry(&table, "uid:1000", "sign_event", "main", "nostr", PROMPT_EVERY_REQUEST);
|
||||
rc = policy_check(&table, "uid:1000", "sign_event", "main", "nostr");
|
||||
check_condition("every_request returns POLICY_PROMPT", rc == POLICY_PROMPT);
|
||||
|
||||
/* First matching entry wins */
|
||||
policy_table_init(&table);
|
||||
add_single_entry(&table, "uid:1000", "sign_event", "main", "nostr", PROMPT_DENY);
|
||||
add_single_entry(&table, "uid:1000", "sign_event", "main", "nostr", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:1000", "sign_event", "main", "nostr");
|
||||
check_condition("first matching entry wins", rc == POLICY_DENY);
|
||||
|
||||
printf("%d/%d tests passed\n", g_passes, g_total);
|
||||
return (g_passes == g_total) ? 0 : 1;
|
||||
}
|
||||
135
tests/test_role_table.c
Normal file
135
tests/test_role_table.c
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "../src/role_table.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int g_failures = 0;
|
||||
|
||||
static void check_condition(const char *name, int condition) {
|
||||
if (condition) {
|
||||
printf("PASS: %s\n", name);
|
||||
} else {
|
||||
printf("FAIL: %s\n", name);
|
||||
g_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
static role_entry_t make_nostr_entry(const char *name, const char *purpose, const char *curve, int idx) {
|
||||
role_entry_t e;
|
||||
|
||||
memset(&e, 0, sizeof(e));
|
||||
strncpy(e.name, name, sizeof(e.name) - 1);
|
||||
strncpy(e.purpose_str, purpose, sizeof(e.purpose_str) - 1);
|
||||
strncpy(e.curve_str, curve, sizeof(e.curve_str) - 1);
|
||||
e.purpose = role_purpose_from_str(e.purpose_str);
|
||||
e.curve = role_curve_from_str(e.curve_str);
|
||||
e.selector_type = SELECTOR_NOSTR_INDEX;
|
||||
e.nostr_index = idx;
|
||||
e.derived = 0;
|
||||
return e;
|
||||
}
|
||||
|
||||
static role_entry_t make_path_entry(const char *name, const char *purpose, const char *curve, const char *path) {
|
||||
role_entry_t e;
|
||||
|
||||
memset(&e, 0, sizeof(e));
|
||||
strncpy(e.name, name, sizeof(e.name) - 1);
|
||||
strncpy(e.purpose_str, purpose, sizeof(e.purpose_str) - 1);
|
||||
strncpy(e.curve_str, curve, sizeof(e.curve_str) - 1);
|
||||
strncpy(e.role_path, path, sizeof(e.role_path) - 1);
|
||||
e.purpose = role_purpose_from_str(e.purpose_str);
|
||||
e.curve = role_curve_from_str(e.curve_str);
|
||||
e.selector_type = SELECTOR_ROLE_PATH;
|
||||
e.nostr_index = -1;
|
||||
e.derived = 0;
|
||||
return e;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
role_table_t table;
|
||||
role_entry_t e_main;
|
||||
role_entry_t e_throwaway;
|
||||
role_entry_t e_btc;
|
||||
role_entry_t e_ssh;
|
||||
role_entry_t *found;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
role_table_init(&table);
|
||||
check_condition("init count==0", table.count == 0);
|
||||
|
||||
e_main = make_nostr_entry("main", "nostr", "secp256k1", 0);
|
||||
rc = role_table_add(&table, &e_main);
|
||||
check_condition("add main returns 0", rc == 0);
|
||||
|
||||
e_throwaway = make_nostr_entry("throwaway", "nostr", "secp256k1", 1);
|
||||
rc = role_table_add(&table, &e_throwaway);
|
||||
check_condition("add throwaway returns 0", rc == 0);
|
||||
|
||||
e_btc = make_path_entry("btc_savings", "bitcoin", "secp256k1", "m/84'/0'/0'/0/0");
|
||||
rc = role_table_add(&table, &e_btc);
|
||||
check_condition("add btc_savings returns 0", rc == 0);
|
||||
|
||||
e_ssh = make_path_entry("ssh_key", "ssh", "ed25519", "m/44'/822'/0'/0/0");
|
||||
rc = role_table_add(&table, &e_ssh);
|
||||
check_condition("add ssh_key returns 0", rc == 0);
|
||||
|
||||
found = role_table_find_by_name(&table, "main");
|
||||
check_condition("find_by_name(main) not NULL", found != NULL);
|
||||
check_condition("find_by_name(main) purpose nostr", found != NULL && found->purpose == PURPOSE_NOSTR);
|
||||
|
||||
found = role_table_find_by_nostr_index(&table, 1);
|
||||
check_condition("find_by_nostr_index(1) is throwaway", found != NULL && strcmp(found->name, "throwaway") == 0);
|
||||
|
||||
found = role_table_find_by_path(&table, "m/84'/0'/0'/0/0");
|
||||
check_condition("find_by_path btc path is btc_savings", found != NULL && strcmp(found->name, "btc_savings") == 0);
|
||||
|
||||
found = role_table_get_default(&table);
|
||||
check_condition("get_default() is main", found != NULL && strcmp(found->name, "main") == 0);
|
||||
|
||||
rc = role_table_add(&table, &e_main);
|
||||
check_condition("duplicate name rejection returns -2", rc == -2);
|
||||
|
||||
role_table_init(&table);
|
||||
for (i = 0; i < ROLE_TABLE_MAX_ENTRIES; ++i) {
|
||||
role_entry_t e;
|
||||
char name_buf[ROLE_NAME_MAX];
|
||||
|
||||
memset(&e, 0, sizeof(e));
|
||||
snprintf(name_buf, sizeof(name_buf), "role_%d", i);
|
||||
strncpy(e.name, name_buf, sizeof(e.name) - 1);
|
||||
strncpy(e.purpose_str, "nostr", sizeof(e.purpose_str) - 1);
|
||||
strncpy(e.curve_str, "secp256k1", sizeof(e.curve_str) - 1);
|
||||
e.purpose = PURPOSE_NOSTR;
|
||||
e.curve = CURVE_SECP256K1;
|
||||
e.selector_type = SELECTOR_NOSTR_INDEX;
|
||||
e.nostr_index = i;
|
||||
|
||||
rc = role_table_add(&table, &e);
|
||||
check_condition("fill table role add returns 0", rc == 0);
|
||||
}
|
||||
|
||||
{
|
||||
role_entry_t overflow;
|
||||
|
||||
memset(&overflow, 0, sizeof(overflow));
|
||||
strncpy(overflow.name, "overflow", sizeof(overflow.name) - 1);
|
||||
strncpy(overflow.purpose_str, "nostr", sizeof(overflow.purpose_str) - 1);
|
||||
strncpy(overflow.curve_str, "secp256k1", sizeof(overflow.curve_str) - 1);
|
||||
overflow.purpose = PURPOSE_NOSTR;
|
||||
overflow.curve = CURVE_SECP256K1;
|
||||
overflow.selector_type = SELECTOR_NOSTR_INDEX;
|
||||
overflow.nostr_index = 999;
|
||||
|
||||
rc = role_table_add(&table, &overflow);
|
||||
check_condition("table full rejection returns -1", rc == -1);
|
||||
}
|
||||
|
||||
if (g_failures == 0) {
|
||||
printf("ALL TESTS PASSED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("TESTS FAILED: %d\n", g_failures);
|
||||
return 1;
|
||||
}
|
||||
170
tests/test_selector.c
Normal file
170
tests/test_selector.c
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "../src/selector.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int g_passes = 0;
|
||||
static int g_total = 0;
|
||||
|
||||
static void check_condition(const char *name, int condition) {
|
||||
g_total++;
|
||||
if (condition) {
|
||||
printf("PASS: %s\n", name);
|
||||
g_passes++;
|
||||
} else {
|
||||
printf("FAIL: %s\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
static role_entry_t make_nostr_entry(const char *name, const char *purpose, const char *curve, int idx) {
|
||||
role_entry_t e;
|
||||
|
||||
memset(&e, 0, sizeof(e));
|
||||
strncpy(e.name, name, sizeof(e.name) - 1);
|
||||
strncpy(e.purpose_str, purpose, sizeof(e.purpose_str) - 1);
|
||||
strncpy(e.curve_str, curve, sizeof(e.curve_str) - 1);
|
||||
e.purpose = role_purpose_from_str(e.purpose_str);
|
||||
e.curve = role_curve_from_str(e.curve_str);
|
||||
e.selector_type = SELECTOR_NOSTR_INDEX;
|
||||
e.nostr_index = idx;
|
||||
e.derived = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static role_entry_t make_path_entry(const char *name, const char *purpose, const char *curve, const char *path) {
|
||||
role_entry_t e;
|
||||
|
||||
memset(&e, 0, sizeof(e));
|
||||
strncpy(e.name, name, sizeof(e.name) - 1);
|
||||
strncpy(e.purpose_str, purpose, sizeof(e.purpose_str) - 1);
|
||||
strncpy(e.curve_str, curve, sizeof(e.curve_str) - 1);
|
||||
strncpy(e.role_path, path, sizeof(e.role_path) - 1);
|
||||
e.purpose = role_purpose_from_str(e.purpose_str);
|
||||
e.curve = role_curve_from_str(e.curve_str);
|
||||
e.selector_type = SELECTOR_ROLE_PATH;
|
||||
e.nostr_index = -1;
|
||||
e.derived = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static void build_standard_table(role_table_t *table) {
|
||||
role_entry_t e_main;
|
||||
role_entry_t e_throwaway;
|
||||
role_entry_t e_btc;
|
||||
|
||||
role_table_init(table);
|
||||
|
||||
e_main = make_nostr_entry("main", "nostr", "secp256k1", 0);
|
||||
e_throwaway = make_nostr_entry("throwaway", "nostr", "secp256k1", 1);
|
||||
e_btc = make_path_entry("btc_savings", "bitcoin", "secp256k1", "m/84'/0'/0'/0/0");
|
||||
|
||||
role_table_add(table, &e_main);
|
||||
role_table_add(table, &e_throwaway);
|
||||
role_table_add(table, &e_btc);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
role_table_t table;
|
||||
role_table_t no_main_table;
|
||||
selector_request_t req;
|
||||
role_entry_t *out;
|
||||
int rc;
|
||||
|
||||
build_standard_table(&table);
|
||||
|
||||
role_table_init(&no_main_table);
|
||||
{
|
||||
role_entry_t e_throwaway = make_nostr_entry("throwaway", "nostr", "secp256k1", 1);
|
||||
role_entry_t e_btc = make_path_entry("btc_savings", "bitcoin", "secp256k1", "m/84'/0'/0'/0/0");
|
||||
role_table_add(&no_main_table, &e_throwaway);
|
||||
role_table_add(&no_main_table, &e_btc);
|
||||
}
|
||||
|
||||
/* 1. No selector given, "main" exists -> resolves to "main" */
|
||||
selector_request_init(&req);
|
||||
out = NULL;
|
||||
rc = selector_resolve(&req, &table, &out);
|
||||
check_condition("default resolves to main", rc == SELECTOR_OK && out != NULL && strcmp(out->name, "main") == 0);
|
||||
|
||||
/* 2. No selector given, no "main" role -> SELECTOR_ERR_NO_DEFAULT */
|
||||
selector_request_init(&req);
|
||||
out = (role_entry_t *)0x1;
|
||||
rc = selector_resolve(&req, &no_main_table, &out);
|
||||
check_condition("no default role returns SELECTOR_ERR_NO_DEFAULT", rc == SELECTOR_ERR_NO_DEFAULT && out == NULL);
|
||||
|
||||
/* 3. role = "throwaway" -> resolves to "throwaway" */
|
||||
selector_request_init(&req);
|
||||
req.has_role = 1;
|
||||
strncpy(req.role_name, "throwaway", sizeof(req.role_name) - 1);
|
||||
out = NULL;
|
||||
rc = selector_resolve(&req, &table, &out);
|
||||
check_condition("role selector resolves throwaway", rc == SELECTOR_OK && out != NULL && strcmp(out->name, "throwaway") == 0);
|
||||
|
||||
/* 4. nostr_index = 1 -> resolves to nostr_index == 1 */
|
||||
selector_request_init(&req);
|
||||
req.has_nostr_index = 1;
|
||||
req.nostr_index = 1;
|
||||
out = NULL;
|
||||
rc = selector_resolve(&req, &table, &out);
|
||||
check_condition("nostr_index selector resolves index 1", rc == SELECTOR_OK && out != NULL && out->nostr_index == 1);
|
||||
|
||||
/* 5. role_path = m/84'/0'/0'/0/0 -> resolves to btc_savings */
|
||||
selector_request_init(&req);
|
||||
req.has_role_path = 1;
|
||||
strncpy(req.role_path, "m/84'/0'/0'/0/0", sizeof(req.role_path) - 1);
|
||||
out = NULL;
|
||||
rc = selector_resolve(&req, &table, &out);
|
||||
check_condition("role_path selector resolves btc_savings", rc == SELECTOR_OK && out != NULL && strcmp(out->name, "btc_savings") == 0);
|
||||
|
||||
/* 6. role + nostr_index both set -> SELECTOR_ERR_AMBIGUOUS */
|
||||
selector_request_init(&req);
|
||||
req.has_role = 1;
|
||||
strncpy(req.role_name, "main", sizeof(req.role_name) - 1);
|
||||
req.has_nostr_index = 1;
|
||||
req.nostr_index = 0;
|
||||
out = (role_entry_t *)0x1;
|
||||
rc = selector_resolve(&req, &table, &out);
|
||||
check_condition("role and nostr_index set is ambiguous", rc == SELECTOR_ERR_AMBIGUOUS && out == NULL);
|
||||
|
||||
/* 7. role = nonexistent -> SELECTOR_ERR_NOT_FOUND */
|
||||
selector_request_init(&req);
|
||||
req.has_role = 1;
|
||||
strncpy(req.role_name, "nonexistent", sizeof(req.role_name) - 1);
|
||||
out = (role_entry_t *)0x1;
|
||||
rc = selector_resolve(&req, &table, &out);
|
||||
check_condition("unknown role returns SELECTOR_ERR_NOT_FOUND", rc == SELECTOR_ERR_NOT_FOUND && out == NULL);
|
||||
|
||||
/* 8. nostr_index = 99 -> SELECTOR_ERR_NOT_FOUND */
|
||||
selector_request_init(&req);
|
||||
req.has_nostr_index = 1;
|
||||
req.nostr_index = 99;
|
||||
out = (role_entry_t *)0x1;
|
||||
rc = selector_resolve(&req, &table, &out);
|
||||
check_condition("unknown nostr_index returns SELECTOR_ERR_NOT_FOUND", rc == SELECTOR_ERR_NOT_FOUND && out == NULL);
|
||||
|
||||
/* 9. role_path = m/99'/0'/0' -> SELECTOR_ERR_NOT_FOUND */
|
||||
selector_request_init(&req);
|
||||
req.has_role_path = 1;
|
||||
strncpy(req.role_path, "m/99'/0'/0'", sizeof(req.role_path) - 1);
|
||||
out = (role_entry_t *)0x1;
|
||||
rc = selector_resolve(&req, &table, &out);
|
||||
check_condition("unknown role_path returns SELECTOR_ERR_NOT_FOUND", rc == SELECTOR_ERR_NOT_FOUND && out == NULL);
|
||||
|
||||
/* 10. all three selectors set -> SELECTOR_ERR_AMBIGUOUS */
|
||||
selector_request_init(&req);
|
||||
req.has_role = 1;
|
||||
strncpy(req.role_name, "main", sizeof(req.role_name) - 1);
|
||||
req.has_nostr_index = 1;
|
||||
req.nostr_index = 0;
|
||||
req.has_role_path = 1;
|
||||
strncpy(req.role_path, "m/84'/0'/0'/0/0", sizeof(req.role_path) - 1);
|
||||
out = (role_entry_t *)0x1;
|
||||
rc = selector_resolve(&req, &table, &out);
|
||||
check_condition("all selectors set is ambiguous", rc == SELECTOR_ERR_AMBIGUOUS && out == NULL);
|
||||
|
||||
printf("%d/10 tests passed\n", g_passes);
|
||||
|
||||
return (g_passes == 10 && g_total == 10) ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user