Files
n_signer/tests/test_auth_envelope.c

228 lines
8.2 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <cJSON.h>
#include <nostr_core/nip001.h>
#include <nostr_core/utils.h>
#include "../src/auth_envelope.h"
static int g_failures = 0;
static void check_condition(const char *name, int ok) {
if (ok) {
printf("[PASS] %s\n", name);
} else {
printf("[FAIL] %s\n", name);
g_failures++;
}
}
static int compute_params_hash_hex(cJSON *params, char out_hex[65]) {
char *compact;
unsigned char hash[32];
if (params == NULL || out_hex == NULL) {
return -1;
}
compact = cJSON_PrintUnformatted(params);
if (compact == NULL) {
return -1;
}
if (nostr_sha256((const unsigned char *)compact, strlen(compact), hash) != 0) {
free(compact);
return -1;
}
nostr_bytes_to_hex(hash, 32, out_hex);
out_hex[64] = '\0';
free(compact);
return 0;
}
static cJSON *tag_pair(const char *k, const char *v) {
cJSON *arr = cJSON_CreateArray();
cJSON_AddItemToArray(arr, cJSON_CreateString(k));
cJSON_AddItemToArray(arr, cJSON_CreateString(v));
return arr;
}
static char *make_request_json(const unsigned char privkey[32],
const char *req_id,
const char *method,
int created_at) {
cJSON *params = cJSON_CreateArray();
cJSON *options = cJSON_CreateObject();
cJSON *tags = cJSON_CreateArray();
cJSON *auth;
cJSON *root;
char body_hash[65];
char *out;
cJSON_AddItemToArray(params, cJSON_CreateString("{\"kind\":1,\"content\":\"x\",\"tags\":[],\"created_at\":1700000000}"));
cJSON_AddStringToObject(options, "role", "main");
cJSON_AddItemToArray(params, options);
if (compute_params_hash_hex(params, body_hash) != 0) {
cJSON_Delete(params);
return NULL;
}
cJSON_AddItemToArray(tags, tag_pair("nsigner_rpc", req_id));
cJSON_AddItemToArray(tags, tag_pair("nsigner_method", method));
cJSON_AddItemToArray(tags, tag_pair("nsigner_body_hash", body_hash));
auth = nostr_create_and_sign_event(AUTH_EVENT_KIND, "test-client", tags, privkey, (time_t)created_at);
if (auth == NULL) {
cJSON_Delete(params);
return NULL;
}
root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "id", req_id);
cJSON_AddStringToObject(root, "method", method);
cJSON_AddItemToObject(root, "params", params);
cJSON_AddItemToObject(root, "auth", auth);
out = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
return out;
}
int main(void) {
auth_nonce_cache_t cache;
unsigned char privkey[32] = {
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
char pubkey_hex[65];
char label[64];
const char *err_msg = NULL;
int err_code = 0;
char *req;
check_condition("nostr_crypto_init", nostr_crypto_init() == 0);
auth_nonce_cache_init(&cache);
req = make_request_json(privkey, "req-1", "nostr_sign_event", (int)time(NULL));
check_condition("build valid request", req != NULL);
if (req != NULL) {
check_condition("valid auth envelope accepted",
auth_envelope_verify_request(req,
&cache,
AUTH_DEFAULT_SKEW_SECONDS,
pubkey_hex,
sizeof(pubkey_hex),
label,
sizeof(label),
&err_code,
&err_msg) == 0);
check_condition("pubkey output length 64", strlen(pubkey_hex) == 64);
free(req);
}
check_condition("missing auth rejected with required code",
auth_envelope_verify_request("{\"id\":\"x\",\"method\":\"get_public_key\",\"params\":[]}",
&cache,
AUTH_DEFAULT_SKEW_SECONDS,
pubkey_hex,
sizeof(pubkey_hex),
label,
sizeof(label),
&err_code,
&err_msg) != 0 &&
err_code == AUTH_ERR_ENVELOPE_REQUIRED);
req = make_request_json(privkey, "req-2", "get_public_key", (int)time(NULL));
check_condition("build second request", req != NULL);
if (req != NULL) {
cJSON *root = cJSON_Parse(req);
cJSON *method_item;
char *tampered;
check_condition("parse second request", root != NULL);
method_item = (root != NULL) ? cJSON_GetObjectItemCaseSensitive(root, "method") : NULL;
if (method_item != NULL) {
cJSON_SetValuestring(method_item, "nostr_sign_event");
}
tampered = (root != NULL) ? cJSON_PrintUnformatted(root) : NULL;
cJSON_Delete(root);
check_condition("tamper method mismatch rejected",
tampered != NULL &&
auth_envelope_verify_request(tampered,
&cache,
AUTH_DEFAULT_SKEW_SECONDS,
pubkey_hex,
sizeof(pubkey_hex),
label,
sizeof(label),
&err_code,
&err_msg) != 0 &&
err_code == AUTH_ERR_ENVELOPE_MISMATCH);
free(tampered);
free(req);
}
req = make_request_json(privkey, "req-3", "nostr_sign_event", (int)time(NULL));
check_condition("build replay request", req != NULL);
if (req != NULL) {
int first_ok = auth_envelope_verify_request(req,
&cache,
AUTH_DEFAULT_SKEW_SECONDS,
pubkey_hex,
sizeof(pubkey_hex),
label,
sizeof(label),
&err_code,
&err_msg);
int second_ok = auth_envelope_verify_request(req,
&cache,
AUTH_DEFAULT_SKEW_SECONDS,
pubkey_hex,
sizeof(pubkey_hex),
label,
sizeof(label),
&err_code,
&err_msg);
check_condition("first replay request accepted", first_ok == 0);
check_condition("second replay request rejected", second_ok != 0 && err_code == AUTH_ERR_REPLAY_DETECTED);
free(req);
}
req = make_request_json(privkey, "req-4", "nostr_sign_event", (int)time(NULL) - 1000);
check_condition("build stale request", req != NULL);
if (req != NULL) {
check_condition("stale request rejected",
auth_envelope_verify_request(req,
&cache,
AUTH_DEFAULT_SKEW_SECONDS,
pubkey_hex,
sizeof(pubkey_hex),
label,
sizeof(label),
&err_code,
&err_msg) != 0 &&
err_code == AUTH_ERR_ENVELOPE_STALE);
free(req);
}
nostr_crypto_cleanup();
if (g_failures == 0) {
printf("ALL TESTS PASSED\n");
return 0;
}
printf("TESTS FAILED: %d\n", g_failures);
return 1;
}