v0.0.57 - Migrated to unified nostr_ prefixed verb names; removed legacy verb aliases (sign_data, ssh_sign, verify_signature, kem_encapsulate, kem_decapsulate, otp_encrypt, otp_decrypt); split get_public_key into algorithm-based get_public_key and role-based nostr_get_public_key; OTP now selected via algorithm:otp instead of curve:otp; consolidated API docs from api.md into README.md
This commit is contained in:
@@ -78,31 +78,31 @@ else
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "[2/7] sign_event"
|
||||
echo "[2/7] nostr_sign_event"
|
||||
now_ts="$(date +%s)"
|
||||
event_json='{"kind":1,"content":"hello from tests/test.sh","tags":[],"created_at":__TS__}'
|
||||
event_json="${event_json/__TS__/${now_ts}}"
|
||||
escaped_event_json="${event_json//\"/\\\"}"
|
||||
req_sign="{\"id\":\"2\",\"method\":\"sign_event\",\"params\":[\"${escaped_event_json}\",{\"role\":\"main\"}]}"
|
||||
req_sign="{\"id\":\"2\",\"method\":\"nostr_sign_event\",\"params\":[\"${escaped_event_json}\",{\"role\":\"main\"}]}"
|
||||
resp_sign="$(run_request "$req_sign")"
|
||||
echo "request : $req_sign"
|
||||
echo "response: $resp_sign"
|
||||
if printf '%s' "$resp_sign" | grep -q '"result"'; then
|
||||
echo "[ok] sign_event returned result"
|
||||
echo "[ok] nostr_sign_event returned result"
|
||||
else
|
||||
echo "[warn] sign_event did not return result"
|
||||
echo "[warn] nostr_sign_event did not return result"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "[3/7] sign_event with nostr_index=0"
|
||||
req_sign_idx="{\"id\":\"3\",\"method\":\"sign_event\",\"params\":[\"${escaped_event_json}\",{\"nostr_index\":0}]}"
|
||||
echo "[3/7] nostr_sign_event with nostr_index=0"
|
||||
req_sign_idx="{\"id\":\"3\",\"method\":\"nostr_sign_event\",\"params\":[\"${escaped_event_json}\",{\"nostr_index\":0}]}"
|
||||
resp_sign_idx="$(run_request "$req_sign_idx")"
|
||||
echo "request : $req_sign_idx"
|
||||
echo "response: $resp_sign_idx"
|
||||
|
||||
echo
|
||||
echo "[4/7] role selector error case (unknown role)"
|
||||
req_bad_role='{"id":"4","method":"sign_event","params":["{}",{"role":"does_not_exist"}]}'
|
||||
req_bad_role='{"id":"4","method":"nostr_nostr_sign_event","params":["{}",{"role":"does_not_exist"}]}'
|
||||
resp_bad_role="$(run_request "$req_bad_role")"
|
||||
echo "request : $req_bad_role"
|
||||
echo "response: $resp_bad_role"
|
||||
@@ -115,13 +115,13 @@ fi
|
||||
echo
|
||||
echo "[5/7] NIP-04 self round-trip"
|
||||
if [[ -n "$pubkey" ]]; then
|
||||
req_nip04_enc="{\"id\":\"5\",\"method\":\"nip04_encrypt\",\"params\":[\"${pubkey}\",\"hello_nip04_from_test_sh\"]}"
|
||||
req_nip04_enc="{\"id\":\"5\",\"method\":\"nostr_nostr_nip04_encrypt\",\"params\":[\"${pubkey}\",\"hello_nip04_from_test_sh\"]}"
|
||||
resp_nip04_enc="$(run_request "$req_nip04_enc")"
|
||||
echo "request : $req_nip04_enc"
|
||||
echo "response: $resp_nip04_enc"
|
||||
cipher_nip04="$(printf '%s' "$resp_nip04_enc" | extract_result_string)"
|
||||
if [[ -n "$cipher_nip04" ]]; then
|
||||
req_nip04_dec="{\"id\":\"6\",\"method\":\"nip04_decrypt\",\"params\":[\"${pubkey}\",\"${cipher_nip04}\"]}"
|
||||
req_nip04_dec="{\"id\":\"6\",\"method\":\"nostr_nostr_nip04_decrypt\",\"params\":[\"${pubkey}\",\"${cipher_nip04}\"]}"
|
||||
resp_nip04_dec="$(run_request "$req_nip04_dec")"
|
||||
echo "request : $req_nip04_dec"
|
||||
echo "response: $resp_nip04_dec"
|
||||
@@ -140,13 +140,13 @@ fi
|
||||
echo
|
||||
echo "[6/7] NIP-44 self round-trip"
|
||||
if [[ -n "$pubkey" ]]; then
|
||||
req_nip44_enc="{\"id\":\"7\",\"method\":\"nip44_encrypt\",\"params\":[\"${pubkey}\",\"hello_nip44_from_test_sh\"]}"
|
||||
req_nip44_enc="{\"id\":\"7\",\"method\":\"nostr_nostr_nip44_encrypt\",\"params\":[\"${pubkey}\",\"hello_nip44_from_test_sh\"]}"
|
||||
resp_nip44_enc="$(run_request "$req_nip44_enc")"
|
||||
echo "request : $req_nip44_enc"
|
||||
echo "response: $resp_nip44_enc"
|
||||
cipher_nip44="$(printf '%s' "$resp_nip44_enc" | extract_result_string)"
|
||||
if [[ -n "$cipher_nip44" ]]; then
|
||||
req_nip44_dec="{\"id\":\"8\",\"method\":\"nip44_decrypt\",\"params\":[\"${pubkey}\",\"${cipher_nip44}\"]}"
|
||||
req_nip44_dec="{\"id\":\"8\",\"method\":\"nostr_nostr_nip44_decrypt\",\"params\":[\"${pubkey}\",\"${cipher_nip44}\"]}"
|
||||
resp_nip44_dec="$(run_request "$req_nip44_dec")"
|
||||
echo "request : $req_nip44_dec"
|
||||
echo "response: $resp_nip44_dec"
|
||||
|
||||
@@ -74,18 +74,14 @@ role_curve_t role_curve_from_str(const char *s);
|
||||
#define VERB_ENCAPSULATE "encapsulate"
|
||||
#define VERB_DECAPSULATE "decapsulate"
|
||||
#define VERB_DERIVE_SHARED "derive_shared_secret"
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_MINE_EVENT "mine_event"
|
||||
#define VERB_SIGN_DATA "sign_data"
|
||||
#define VERB_VERIFY_SIG "verify_signature"
|
||||
#define VERB_SSH_SIGN "ssh_sign"
|
||||
#define VERB_KEM_ENCAPS "kem_encapsulate"
|
||||
#define VERB_KEM_DECAPS "kem_decapsulate"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_MINE_EVENT "nostr_mine_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
int enforce_verb_role(const char *verb, const role_entry_t *role);
|
||||
const char *enforce_strerror(int err);
|
||||
|
||||
@@ -369,10 +365,10 @@ int main(void) {
|
||||
enforce_verb_algorithm(VERB_DERIVE_SHARED, CRYPTO_ALG_ED25519) == ENFORCE_ERR_ALGORITHM);
|
||||
check("enforce get_public_key + any alg -> OK",
|
||||
enforce_verb_algorithm(VERB_GET_PUBLIC_KEY, CRYPTO_ALG_ED25519) == ENFORCE_OK);
|
||||
check("enforce sign_event + secp256k1 -> OK",
|
||||
enforce_verb_algorithm(VERB_SIGN_EVENT, CRYPTO_ALG_SECP256K1) == ENFORCE_OK);
|
||||
check("enforce sign_event + ed25519 -> ALGORITHM err",
|
||||
enforce_verb_algorithm(VERB_SIGN_EVENT, CRYPTO_ALG_ED25519) == ENFORCE_ERR_ALGORITHM);
|
||||
check("enforce nostr_sign_event + secp256k1 -> OK",
|
||||
enforce_verb_algorithm(VERB_NOSTR_SIGN_EVENT, CRYPTO_ALG_SECP256K1) == ENFORCE_OK);
|
||||
check("enforce nostr_sign_event + ed25519 -> ALGORITHM err",
|
||||
enforce_verb_algorithm(VERB_NOSTR_SIGN_EVENT, CRYPTO_ALG_ED25519) == ENFORCE_ERR_ALGORITHM);
|
||||
|
||||
/* ---- get_public_key with algorithm parameter ---- */
|
||||
resp = dispatcher_handle_request(&g_dispatcher,
|
||||
@@ -588,51 +584,51 @@ int main(void) {
|
||||
resp_has(resp, "algorithm_not_supported_for_verb") || resp_has(resp, "\"code\":1010"));
|
||||
free(resp);
|
||||
|
||||
/* ---- Old verb alias: sign_data with algorithm=ed25519 ---- */
|
||||
/* ---- Verb: sign with algorithm=ed25519 ---- */
|
||||
resp = dispatcher_handle_request(&g_dispatcher,
|
||||
"{\"id\":\"alias1\",\"method\":\"sign_data\",\"params\":[\"68656c6c6f\",{\"algorithm\":\"ed25519\",\"index\":0}]}");
|
||||
check("sign_data alias with algorithm=ed25519 returns result",
|
||||
"{\"id\":\"alias1\",\"method\":\"sign\",\"params\":[\"68656c6c6f\",{\"algorithm\":\"ed25519\",\"index\":0}]}");
|
||||
check("sign with algorithm=ed25519 returns result",
|
||||
resp_has(resp, "\"result\""));
|
||||
check("sign_data alias has signature",
|
||||
check("sign has signature",
|
||||
resp_has(resp, "signature"));
|
||||
free(resp);
|
||||
|
||||
/* ---- Old verb alias: ssh_sign with algorithm=ed25519 ---- */
|
||||
/* ---- Verb: sign (ssh) with algorithm=ed25519 ---- */
|
||||
resp = dispatcher_handle_request(&g_dispatcher,
|
||||
"{\"id\":\"alias2\",\"method\":\"ssh_sign\",\"params\":[\"68656c6c6f\",{\"algorithm\":\"ed25519\",\"index\":0}]}");
|
||||
check("ssh_sign alias with algorithm=ed25519 returns result",
|
||||
"{\"id\":\"alias2\",\"method\":\"sign\",\"params\":[\"68656c6c6f\",{\"algorithm\":\"ed25519\",\"index\":0}]}");
|
||||
check("sign with algorithm=ed25519 returns result",
|
||||
resp_has(resp, "\"result\""));
|
||||
free(resp);
|
||||
|
||||
/* ---- Old verb alias: kem_encapsulate with algorithm=ml-kem-768 ---- */
|
||||
/* ---- Verb: encapsulate with algorithm=ml-kem-768 ---- */
|
||||
resp = dispatcher_handle_request(&g_dispatcher,
|
||||
"{\"id\":\"gpk5\",\"method\":\"get_public_key\",\"params\":[{\"algorithm\":\"ml-kem-768\",\"index\":1}]}");
|
||||
pub_hex = extract_result_field(resp, "public_key");
|
||||
free(resp);
|
||||
if (pub_hex) {
|
||||
snprintf(request, sizeof(request),
|
||||
"{\"id\":\"alias3\",\"method\":\"kem_encapsulate\",\"params\":[\"%s\",{\"algorithm\":\"ml-kem-768\"}]}",
|
||||
"{\"id\":\"alias3\",\"method\":\"encapsulate\",\"params\":[\"%s\",{\"algorithm\":\"ml-kem-768\"}]}",
|
||||
pub_hex);
|
||||
resp = dispatcher_handle_request(&g_dispatcher, request);
|
||||
check("kem_encapsulate alias with algorithm=ml-kem-768 returns result",
|
||||
check("encapsulate with algorithm=ml-kem-768 returns result",
|
||||
resp_has(resp, "\"result\""));
|
||||
check("kem_encapsulate alias has ciphertext",
|
||||
check("encapsulate has ciphertext",
|
||||
resp_has(resp, "ciphertext"));
|
||||
free(resp);
|
||||
free(pub_hex);
|
||||
}
|
||||
|
||||
/* ---- Backward compat: sign_event with role still works ---- */
|
||||
/* ---- nostr_sign_event with role still works ---- */
|
||||
resp = dispatcher_handle_request(&g_dispatcher,
|
||||
"{\"id\":\"bc1\",\"method\":\"sign_event\",\"params\":[\"{\\\"kind\\\":1,\\\"content\\\":\\\"hello\\\",\\\"tags\\\":[]}\",{\"role\":\"main\"}]}");
|
||||
check("sign_event with role=main (backward compat) returns signed event",
|
||||
"{\"id\":\"bc1\",\"method\":\"nostr_sign_event\",\"params\":[\"{\\\"kind\\\":1,\\\"content\\\":\\\"hello\\\",\\\"tags\\\":[]}\",{\"role\":\"main\"}]}");
|
||||
check("nostr_sign_event with role=main returns signed event",
|
||||
resp_has(resp, "pubkey") && resp_has(resp, "sig"));
|
||||
free(resp);
|
||||
|
||||
/* ---- Backward compat: get_public_key with role still works ---- */
|
||||
/* ---- nostr_get_public_key with role still works ---- */
|
||||
resp = dispatcher_handle_request(&g_dispatcher,
|
||||
"{\"id\":\"bc2\",\"method\":\"get_public_key\",\"params\":[{\"role\":\"main\"}]}");
|
||||
check("get_public_key with role=main (backward compat) returns hex",
|
||||
"{\"id\":\"bc2\",\"method\":\"nostr_get_public_key\",\"params\":[{\"role\":\"main\"}]}");
|
||||
check("nostr_get_public_key with role=main returns hex",
|
||||
resp_has(resp, "\"result\""));
|
||||
free(resp);
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ int main(void) {
|
||||
check_condition("nostr_crypto_init", nostr_crypto_init() == 0);
|
||||
auth_nonce_cache_init(&cache);
|
||||
|
||||
req = make_request_json(privkey, "req-1", "sign_event", (int)time(NULL));
|
||||
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",
|
||||
@@ -150,7 +150,7 @@ int main(void) {
|
||||
check_condition("parse second request", root != NULL);
|
||||
method_item = (root != NULL) ? cJSON_GetObjectItemCaseSensitive(root, "method") : NULL;
|
||||
if (method_item != NULL) {
|
||||
cJSON_SetValuestring(method_item, "sign_event");
|
||||
cJSON_SetValuestring(method_item, "nostr_sign_event");
|
||||
}
|
||||
tampered = (root != NULL) ? cJSON_PrintUnformatted(root) : NULL;
|
||||
cJSON_Delete(root);
|
||||
@@ -172,7 +172,7 @@ int main(void) {
|
||||
free(req);
|
||||
}
|
||||
|
||||
req = make_request_json(privkey, "req-3", "sign_event", (int)time(NULL));
|
||||
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,
|
||||
@@ -198,7 +198,7 @@ int main(void) {
|
||||
free(req);
|
||||
}
|
||||
|
||||
req = make_request_json(privkey, "req-4", "sign_event", (int)time(NULL) - 1000);
|
||||
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",
|
||||
|
||||
@@ -223,19 +223,20 @@ const char *selector_strerror(int err);
|
||||
|
||||
|
||||
/* Known verbs */
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
/*
|
||||
* Check whether `verb` is allowed to execute against `role`.
|
||||
* Returns ENFORCE_OK if allowed, or an ENFORCE_ERR_* code.
|
||||
*
|
||||
* The enforcement rules are:
|
||||
* - All nostr verbs (sign_event, get_public_key, nip44_*, nip04_*) require:
|
||||
* - All nostr verbs (nostr_sign_event, nostr_get_public_key, nostr_nip44_*, nostr_nip04_*) require:
|
||||
* purpose == PURPOSE_NOSTR and curve == CURVE_SECP256K1
|
||||
* - Unknown verbs return ENFORCE_ERR_UNKNOWN_VERB (fail-closed).
|
||||
*/
|
||||
@@ -813,51 +814,51 @@ int main(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 1. Valid get_public_key no selector -> default main, real pubkey */
|
||||
/* 1. Valid nostr_get_public_key no selector -> default main, real pubkey */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"1\",\"method\":\"get_public_key\",\"params\":[\"\"]}");
|
||||
check_condition("get_public_key default role returns derived hex",
|
||||
"{\"id\":\"1\",\"method\":\"nostr_get_public_key\",\"params\":[\"\"]}");
|
||||
check_condition("nostr_get_public_key default role returns derived hex",
|
||||
response_has(resp, "\"id\":\"1\"") && !response_has(resp, "not_yet_derived") && response_has(resp, "\"result\":\""));
|
||||
free(resp);
|
||||
|
||||
/* 2. sign_event with role main */
|
||||
/* 2. nostr_sign_event with role main */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"2\",\"method\":\"sign_event\",\"params\":[\"{\\\"kind\\\":1,\\\"content\\\":\\\"hello\\\",\\\"tags\\\":[]}\",{\"role\":\"main\"}]}");
|
||||
check_condition("sign_event with role=main returns signed event",
|
||||
"{\"id\":\"2\",\"method\":\"nostr_sign_event\",\"params\":[\"{\\\"kind\\\":1,\\\"content\\\":\\\"hello\\\",\\\"tags\\\":[]}\",{\"role\":\"main\"}]}");
|
||||
check_condition("nostr_sign_event with role=main returns signed event",
|
||||
response_has(resp, "\"id\":\"2\"") && response_has(resp, "pubkey") && response_has(resp, "sig") && response_has(resp, "created_at"));
|
||||
free(resp);
|
||||
|
||||
/* 3. sign_event with nostr_index 0 */
|
||||
/* 3. nostr_sign_event with nostr_index 0 */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"3\",\"method\":\"sign_event\",\"params\":[\"{\\\"kind\\\":1,\\\"content\\\":\\\"hello2\\\",\\\"tags\\\":[]}\",{\"nostr_index\":0}]}");
|
||||
check_condition("sign_event with nostr_index=0 returns signed event",
|
||||
"{\"id\":\"3\",\"method\":\"nostr_sign_event\",\"params\":[\"{\\\"kind\\\":1,\\\"content\\\":\\\"hello2\\\",\\\"tags\\\":[]}\",{\"nostr_index\":0}]}");
|
||||
check_condition("nostr_sign_event with nostr_index=0 returns signed event",
|
||||
response_has(resp, "\"id\":\"3\"") && response_has(resp, "pubkey") && response_has(resp, "sig") && response_has(resp, "created_at"));
|
||||
free(resp);
|
||||
|
||||
/* 4. ambiguous selector role + nostr_index */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"4\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"main\",\"nostr_index\":0}]}");
|
||||
"{\"id\":\"4\",\"method\":\"nostr_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. unknown role */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"5\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"nonexistent\"}]}");
|
||||
"{\"id\":\"5\",\"method\":\"nostr_sign_event\",\"params\":[\"{}\",{\"role\":\"nonexistent\"}]}");
|
||||
check_condition("unknown role returns 1002 with unknown_role",
|
||||
response_has(resp, "\"id\":\"5\"") && response_has(resp, "\"code\":1002") && response_has(resp, "unknown_role"));
|
||||
free(resp);
|
||||
|
||||
/* 6. purpose mismatch: sign_event against ssh role */
|
||||
/* 6. purpose mismatch: nostr_sign_event against ssh role */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"6\",\"method\":\"sign_event\",\"params\":[\"{}\",{\"role\":\"ssh_key\"}]}");
|
||||
"{\"id\":\"6\",\"method\":\"nostr_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\":[\"{}\"]");
|
||||
"{\"id\":\"7\",\"method\":\"nostr_sign_event\",\"params\":[\"{}\"]");
|
||||
check_condition("invalid JSON returns -32700",
|
||||
response_has(resp, "\"code\":-32700"));
|
||||
free(resp);
|
||||
@@ -872,7 +873,7 @@ int main(void) {
|
||||
/* 9. mnemonic not loaded */
|
||||
mnemonic_unload(&mnemonic);
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"9\",\"method\":\"get_public_key\",\"params\":[\"\"]}");
|
||||
"{\"id\":\"9\",\"method\":\"nostr_get_public_key\",\"params\":[\"\"]}");
|
||||
check_condition("mnemonic not loaded returns 1006",
|
||||
response_has(resp, "\"id\":\"9\"") && response_has(resp, "\"code\":1006"));
|
||||
free(resp);
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
* - x25519 ECDH roundtrip: two sides derive matching shared secret
|
||||
* - x25519 determinism: same seed -> same keypair
|
||||
* - SLIP-0010 derivation: deterministic seed from mnemonic+path
|
||||
* - Integration: derive ed25519 key via crypto_derive_one, sign_data via
|
||||
* - Integration: derive ed25519 key via crypto_derive_one, sign via
|
||||
* dispatcher, verify signature
|
||||
* - Enforcement: sign_data allowed on ssh+ed25519, rejected on nostr+secp256k1
|
||||
* - Enforcement: ssh_sign allowed on ssh+ed25519
|
||||
* - Enforcement: kem_encapsulate/decapsulate rejected on ssh/ed25519
|
||||
* - Enforcement: sign allowed on ssh+ed25519, rejected on nostr+secp256k1
|
||||
* - Enforcement: sign allowed on ssh+ed25519
|
||||
* - Enforcement: encapsulate/decapsulate rejected on ssh/ed25519
|
||||
*/
|
||||
/* NSIGNER_HEADERLESS_DECLS_BEGIN */
|
||||
#include <stddef.h>
|
||||
@@ -150,19 +150,21 @@ const char *selector_strerror(int err);
|
||||
#define ENFORCE_ERR_PURPOSE -1
|
||||
#define ENFORCE_ERR_CURVE -2
|
||||
#define ENFORCE_ERR_UNKNOWN_VERB -3
|
||||
#define ENFORCE_ERR_ALGORITHM -4
|
||||
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_MINE_EVENT "mine_event"
|
||||
#define VERB_SIGN_DATA "sign_data"
|
||||
#define VERB_VERIFY_SIG "verify_signature"
|
||||
#define VERB_SSH_SIGN "ssh_sign"
|
||||
#define VERB_KEM_ENCAPS "kem_encapsulate"
|
||||
#define VERB_KEM_DECAPS "kem_decapsulate"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_SIGN "sign"
|
||||
#define VERB_VERIFY "verify"
|
||||
#define VERB_ENCAPSULATE "encapsulate"
|
||||
#define VERB_DECAPSULATE "decapsulate"
|
||||
#define VERB_DERIVE_SHARED "derive_shared_secret"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_MINE_EVENT "nostr_mine_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
int enforce_verb_role(const char *verb, const role_entry_t *role);
|
||||
const char *enforce_strerror(int err);
|
||||
@@ -692,7 +694,7 @@ int main(void) {
|
||||
mnemonic_unload(&mnemonic_state);
|
||||
}
|
||||
|
||||
/* ---- Integration: sign_data via dispatcher ---- */
|
||||
/* ---- Integration: sign via dispatcher ---- */
|
||||
{
|
||||
role_table_t table;
|
||||
role_entry_t ssh_role;
|
||||
@@ -717,27 +719,27 @@ int main(void) {
|
||||
|
||||
dispatcher_init(&dispatcher, &table, &mnemonic_state, &key_store, &g_alg_key_cache);
|
||||
|
||||
/* sign_data request */
|
||||
/* sign request */
|
||||
snprintf(request, sizeof(request),
|
||||
"{\"id\":\"test1\",\"method\":\"sign_data\",\"params\":[\"%s\",{\"role\":\"ssh_main\"}]}",
|
||||
"{\"id\":\"test1\",\"method\":\"sign\",\"params\":[\"%s\",{\"algorithm\":\"ed25519\",\"index\":0}]}",
|
||||
msg_hex);
|
||||
resp = dispatcher_handle_request(&dispatcher, request);
|
||||
check_condition("sign_data via dispatcher returns result",
|
||||
check_condition("sign via dispatcher returns result",
|
||||
resp != NULL && response_has(resp, "\"result\""));
|
||||
check_condition("sign_data result contains signature",
|
||||
check_condition("sign result contains signature",
|
||||
resp != NULL && response_has(resp, "signature"));
|
||||
check_condition("sign_data result contains algorithm ed25519",
|
||||
check_condition("sign result contains algorithm ed25519",
|
||||
resp != NULL && response_has(resp, "ed25519"));
|
||||
free(resp);
|
||||
|
||||
/* ssh_sign request */
|
||||
/* sign request */
|
||||
snprintf(request, sizeof(request),
|
||||
"{\"id\":\"test2\",\"method\":\"ssh_sign\",\"params\":[\"%s\",{\"role\":\"ssh_main\"}]}",
|
||||
"{\"id\":\"test2\",\"method\":\"sign\",\"params\":[\"%s\",{\"algorithm\":\"ed25519\",\"index\":0}]}",
|
||||
msg_hex);
|
||||
resp = dispatcher_handle_request(&dispatcher, request);
|
||||
check_condition("ssh_sign via dispatcher returns result",
|
||||
check_condition("sign via dispatcher returns result",
|
||||
resp != NULL && response_has(resp, "\"result\""));
|
||||
check_condition("ssh_sign result contains signature",
|
||||
check_condition("sign result contains signature",
|
||||
resp != NULL && response_has(resp, "signature"));
|
||||
free(resp);
|
||||
|
||||
@@ -745,38 +747,39 @@ int main(void) {
|
||||
mnemonic_unload(&mnemonic_state);
|
||||
}
|
||||
|
||||
/* ---- Enforcement: sign_data on ssh+ed25519 ---- */
|
||||
/* ---- Enforcement: sign on ssh+ed25519 ---- */
|
||||
{
|
||||
role_entry_t ssh_ed = make_ssh_ed25519_entry("ssh", 0);
|
||||
role_entry_t nostr_secp = make_nostr_secp_entry("nostr", 0);
|
||||
role_entry_t pq_kem = make_pq_kem_entry("kem", 0);
|
||||
|
||||
check_condition("enforce sign_data + ssh/ed25519 -> OK",
|
||||
enforce_verb_role(VERB_SIGN_DATA, &ssh_ed) == ENFORCE_OK);
|
||||
/* sign/verify are algorithm-based now: checked via enforce_verb_algorithm(). */
|
||||
check_condition("enforce sign + ed25519 -> OK",
|
||||
enforce_verb_algorithm(VERB_SIGN, CRYPTO_ALG_ED25519) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce sign_data + nostr/secp256k1 -> PURPOSE err",
|
||||
enforce_verb_role(VERB_SIGN_DATA, &nostr_secp) == ENFORCE_ERR_PURPOSE);
|
||||
check_condition("enforce sign + secp256k1 -> OK",
|
||||
enforce_verb_algorithm(VERB_SIGN, CRYPTO_ALG_SECP256K1) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce ssh_sign + ssh/ed25519 -> OK",
|
||||
enforce_verb_role(VERB_SSH_SIGN, &ssh_ed) == ENFORCE_OK);
|
||||
check_condition("enforce verify + ed25519 -> OK",
|
||||
enforce_verb_algorithm(VERB_VERIFY, CRYPTO_ALG_ED25519) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce ssh_sign + nostr/secp256k1 -> PURPOSE err",
|
||||
enforce_verb_role(VERB_SSH_SIGN, &nostr_secp) == ENFORCE_ERR_PURPOSE);
|
||||
check_condition("enforce sign + ml-kem-768 -> ALGORITHM err",
|
||||
enforce_verb_algorithm(VERB_SIGN, CRYPTO_ALG_ML_KEM_768) == ENFORCE_ERR_ALGORITHM);
|
||||
|
||||
check_condition("enforce sign_event + ssh/ed25519 -> PURPOSE err",
|
||||
enforce_verb_role(VERB_SIGN_EVENT, &ssh_ed) == ENFORCE_ERR_PURPOSE);
|
||||
check_condition("enforce nostr_sign_event + ssh/ed25519 -> PURPOSE err",
|
||||
enforce_verb_role(VERB_NOSTR_SIGN_EVENT, &ssh_ed) == ENFORCE_ERR_PURPOSE);
|
||||
|
||||
check_condition("enforce kem_encapsulate + pq-kem/ml-kem-768 -> OK",
|
||||
enforce_verb_role(VERB_KEM_ENCAPS, &pq_kem) == ENFORCE_OK);
|
||||
check_condition("enforce encapsulate + ml-kem-768 -> OK",
|
||||
enforce_verb_algorithm(VERB_ENCAPSULATE, CRYPTO_ALG_ML_KEM_768) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce kem_decapsulate + pq-kem/ml-kem-768 -> OK",
|
||||
enforce_verb_role(VERB_KEM_DECAPS, &pq_kem) == ENFORCE_OK);
|
||||
check_condition("enforce decapsulate + ml-kem-768 -> OK",
|
||||
enforce_verb_algorithm(VERB_DECAPSULATE, CRYPTO_ALG_ML_KEM_768) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce kem_encapsulate + ssh/ed25519 -> PURPOSE err",
|
||||
enforce_verb_role(VERB_KEM_ENCAPS, &ssh_ed) == ENFORCE_ERR_PURPOSE);
|
||||
check_condition("enforce encapsulate + ed25519 -> ALGORITHM err",
|
||||
enforce_verb_algorithm(VERB_ENCAPSULATE, CRYPTO_ALG_ED25519) == ENFORCE_ERR_ALGORITHM);
|
||||
}
|
||||
|
||||
/* ---- Dispatcher: kem_encapsulate with invalid pubkey length ---- */
|
||||
/* ---- Dispatcher: encapsulate with invalid pubkey length ---- */
|
||||
{
|
||||
role_table_t table;
|
||||
role_entry_t kem_role;
|
||||
@@ -784,7 +787,7 @@ int main(void) {
|
||||
key_store_t key_store;
|
||||
dispatcher_ctx_t dispatcher;
|
||||
char *resp;
|
||||
const char *request = "{\"id\":\"k1\",\"method\":\"kem_encapsulate\",\"params\":[\"00\",{\"role\":\"kem_main\"}]}";
|
||||
const char *request = "{\"id\":\"k1\",\"method\":\"encapsulate\",\"params\":[\"00\",{\"algorithm\":\"ml-kem-768\",\"index\":0}]}";
|
||||
|
||||
role_table_init(&table);
|
||||
kem_role = make_pq_kem_entry("kem_main", 0);
|
||||
@@ -798,11 +801,11 @@ int main(void) {
|
||||
alg_key_cache_init(&g_alg_key_cache);
|
||||
dispatcher_init(&dispatcher, &table, &mnemonic_state, &key_store, &g_alg_key_cache);
|
||||
|
||||
/* kem_encapsulate is now implemented (Phase 5). With a too-short
|
||||
/* encapsulate is now implemented (Phase 5). With a too-short
|
||||
* pubkey hex ("00" = 1 byte, but ML-KEM-768 needs 1184 bytes),
|
||||
* the dispatcher should return an invalid_pubkey_length error. */
|
||||
resp = dispatcher_handle_request(&dispatcher, request);
|
||||
check_condition("kem_encapsulate with short pubkey returns error",
|
||||
check_condition("encapsulate with short pubkey returns error",
|
||||
resp != NULL && response_has(resp, "\"error\""));
|
||||
free(resp);
|
||||
|
||||
@@ -810,7 +813,7 @@ int main(void) {
|
||||
mnemonic_unload(&mnemonic_state);
|
||||
}
|
||||
|
||||
/* ---- Dispatcher: verify_signature roundtrip ---- */
|
||||
/* ---- Dispatcher: verify roundtrip ---- */
|
||||
{
|
||||
role_table_t table;
|
||||
role_entry_t ssh_role;
|
||||
@@ -839,10 +842,10 @@ int main(void) {
|
||||
|
||||
/* Sign */
|
||||
snprintf(sign_req, sizeof(sign_req),
|
||||
"{\"id\":\"s1\",\"method\":\"sign_data\",\"params\":[\"%s\",{\"role\":\"ssh_main\"}]}",
|
||||
"{\"id\":\"s1\",\"method\":\"sign\",\"params\":[\"%s\",{\"algorithm\":\"ed25519\",\"index\":0}]}",
|
||||
msg_hex);
|
||||
sign_resp = dispatcher_handle_request(&dispatcher, sign_req);
|
||||
check_condition("verify roundtrip: sign_data succeeds", sign_resp != NULL);
|
||||
check_condition("verify roundtrip: sign succeeds", sign_resp != NULL);
|
||||
|
||||
/* Extract signature from result */
|
||||
if (sign_resp != NULL) {
|
||||
@@ -867,28 +870,28 @@ int main(void) {
|
||||
/* Verify */
|
||||
if (sig_str != NULL) {
|
||||
snprintf(verify_req, sizeof(verify_req),
|
||||
"{\"id\":\"v1\",\"method\":\"verify_signature\",\"params\":[\"%s\",\"%s\",{\"role\":\"ssh_main\"}]}",
|
||||
"{\"id\":\"v1\",\"method\":\"verify\",\"params\":[\"%s\",\"%s\",{\"algorithm\":\"ed25519\",\"index\":0}]}",
|
||||
msg_hex, sig_str);
|
||||
verify_resp = dispatcher_handle_request(&dispatcher, verify_req);
|
||||
check_condition("verify_signature returns valid:true",
|
||||
check_condition("verify returns valid:true",
|
||||
verify_resp != NULL && response_has(verify_resp, "valid") &&
|
||||
response_has(verify_resp, "true"));
|
||||
free(verify_resp);
|
||||
|
||||
/* Verify with wrong message */
|
||||
snprintf(verify_req, sizeof(verify_req),
|
||||
"{\"id\":\"v2\",\"method\":\"verify_signature\",\"params\":[\"00ff\",\"%s\",{\"role\":\"ssh_main\"}]}",
|
||||
"{\"id\":\"v2\",\"method\":\"verify\",\"params\":[\"00ff\",\"%s\",{\"algorithm\":\"ed25519\",\"index\":0}]}",
|
||||
sig_str);
|
||||
verify_resp = dispatcher_handle_request(&dispatcher, verify_req);
|
||||
check_condition("verify_signature wrong msg returns valid:false",
|
||||
check_condition("verify wrong msg returns valid:false",
|
||||
verify_resp != NULL && response_has(verify_resp, "valid") &&
|
||||
response_has(verify_resp, "false"));
|
||||
free(verify_resp);
|
||||
|
||||
free((void *)sig_str);
|
||||
} else {
|
||||
check_condition("verify_signature returns valid:true", 0);
|
||||
check_condition("verify_signature wrong msg returns valid:false", 0);
|
||||
check_condition("verify returns valid:true", 0);
|
||||
check_condition("verify wrong msg returns valid:false", 0);
|
||||
}
|
||||
|
||||
free(sign_resp);
|
||||
|
||||
@@ -220,19 +220,20 @@ const char *selector_strerror(int err);
|
||||
|
||||
|
||||
/* Known verbs */
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
/*
|
||||
* Check whether `verb` is allowed to execute against `role`.
|
||||
* Returns ENFORCE_OK if allowed, or an ENFORCE_ERR_* code.
|
||||
*
|
||||
* The enforcement rules are:
|
||||
* - All nostr verbs (sign_event, get_public_key, nip44_*, nip04_*) require:
|
||||
* - All nostr verbs (nostr_sign_event, nostr_get_public_key, nostr_nip44_*, nostr_nip04_*) require:
|
||||
* purpose == PURPOSE_NOSTR and curve == CURVE_SECP256K1
|
||||
* - Unknown verbs return ENFORCE_ERR_UNKNOWN_VERB (fail-closed).
|
||||
*/
|
||||
@@ -739,43 +740,43 @@ int main(void) {
|
||||
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
|
||||
"nostr_sign_event + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_NOSTR_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
|
||||
"nostr_get_public_key + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_NOSTR_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
|
||||
"nostr_nip44_encrypt + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_NOSTR_NIP44_ENCRYPT, &nostr_secp) == ENFORCE_OK
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"nip44_decrypt + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_NIP44_DECRYPT, &nostr_secp) == ENFORCE_OK
|
||||
"nostr_nip44_decrypt + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_NOSTR_NIP44_DECRYPT, &nostr_secp) == ENFORCE_OK
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"nip04_encrypt + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_NIP04_ENCRYPT, &nostr_secp) == ENFORCE_OK
|
||||
"nostr_nip04_encrypt + nostr/secp256k1 -> ENFORCE_OK",
|
||||
enforce_verb_role(VERB_NOSTR_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
|
||||
"nostr_sign_event + bitcoin/secp256k1 -> ENFORCE_ERR_PURPOSE",
|
||||
enforce_verb_role(VERB_NOSTR_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
|
||||
"nostr_sign_event + nostr/ed25519 -> ENFORCE_ERR_CURVE",
|
||||
enforce_verb_role(VERB_NOSTR_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
|
||||
"nostr_sign_event + ssh/ed25519 -> ENFORCE_ERR_PURPOSE",
|
||||
enforce_verb_role(VERB_NOSTR_SIGN_EVENT, &ssh_ed) == ENFORCE_ERR_PURPOSE
|
||||
);
|
||||
|
||||
check_condition(
|
||||
@@ -784,8 +785,8 @@ int main(void) {
|
||||
);
|
||||
|
||||
check_condition(
|
||||
"nip44_encrypt + age/x25519 -> ENFORCE_ERR_PURPOSE",
|
||||
enforce_verb_role(VERB_NIP44_ENCRYPT, &age_x) == ENFORCE_ERR_PURPOSE
|
||||
"nostr_nip44_encrypt + age/x25519 -> ENFORCE_ERR_PURPOSE",
|
||||
enforce_verb_role(VERB_NOSTR_NIP44_ENCRYPT, &age_x) == ENFORCE_ERR_PURPOSE
|
||||
);
|
||||
|
||||
printf("%d/10 tests passed\n", g_passes);
|
||||
|
||||
@@ -222,19 +222,20 @@ const char *selector_strerror(int err);
|
||||
|
||||
|
||||
/* Known verbs */
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
/*
|
||||
* Check whether `verb` is allowed to execute against `role`.
|
||||
* Returns ENFORCE_OK if allowed, or an ENFORCE_ERR_* code.
|
||||
*
|
||||
* The enforcement rules are:
|
||||
* - All nostr verbs (sign_event, get_public_key, nip44_*, nip04_*) require:
|
||||
* - All nostr verbs (nostr_sign_event, nostr_get_public_key, nostr_nip44_*, nostr_nip04_*) require:
|
||||
* purpose == PURPOSE_NOSTR and curve == CURVE_SECP256K1
|
||||
* - Unknown verbs return ENFORCE_ERR_UNKNOWN_VERB (fail-closed).
|
||||
*/
|
||||
@@ -1001,13 +1002,13 @@ int main(void) {
|
||||
nsigner_client_free(c);
|
||||
c = NULL;
|
||||
|
||||
/* sign_event (fresh connection) */
|
||||
/* nostr_sign_event (fresh connection) */
|
||||
t = nsigner_transport_open_unix(SOCKET_NAME_A, 5000);
|
||||
if (t != NULL) {
|
||||
c = nsigner_client_new(t);
|
||||
if (c == NULL) {
|
||||
t->close(t);
|
||||
check_condition("connect signer socket for sign_event", 0);
|
||||
check_condition("connect signer socket for nostr_sign_event", 0);
|
||||
} else {
|
||||
params = cJSON_CreateArray();
|
||||
if (params != NULL) {
|
||||
@@ -1018,10 +1019,10 @@ int main(void) {
|
||||
cJSON_AddItemToArray(params, opts);
|
||||
opts = NULL;
|
||||
}
|
||||
if (nsigner_client_call(c, "sign_event", params, &se_result) == NOSTR_SUCCESS) {
|
||||
check_condition("sign_event has result", se_result != NULL);
|
||||
if (nsigner_client_call(c, "nostr_sign_event", params, &se_result) == NOSTR_SUCCESS) {
|
||||
check_condition("nostr_sign_event has result", se_result != NULL);
|
||||
} else {
|
||||
check_condition("sign_event request roundtrip", 0);
|
||||
check_condition("nostr_sign_event request roundtrip", 0);
|
||||
}
|
||||
params = NULL; /* nsigner_client_call took ownership */
|
||||
cJSON_Delete(se_result);
|
||||
@@ -1029,7 +1030,7 @@ int main(void) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
check_condition("connect signer socket for sign_event", 0);
|
||||
check_condition("connect signer socket for nostr_sign_event", 0);
|
||||
}
|
||||
|
||||
cJSON_Delete(params);
|
||||
@@ -1052,7 +1053,7 @@ int main(void) {
|
||||
if (p) { p += 10; strncpy(pub_a, p, 64); pub_a[64]='\0'; }
|
||||
check_condition("single-instance public key request", pub_a[0] != '\0');
|
||||
|
||||
snprintf(req, sizeof(req), "{\"id\":\"5\",\"method\":\"nip04_encrypt\",\"params\":[\"%s\",\"hello_nip04\"]}", pub_a);
|
||||
snprintf(req, sizeof(req), "{\"id\":\"5\",\"method\":\"nostr_nip04_encrypt\",\"params\":[\"%s\",\"hello_nip04\"]}", pub_a);
|
||||
free(resp_a); resp_a = NULL;
|
||||
if (request_roundtrip_to(SOCKET_NAME_A, req, &resp_a) == 0) {
|
||||
p = strstr(resp_a, "\"result\":\"");
|
||||
@@ -1062,7 +1063,7 @@ int main(void) {
|
||||
while (p[i] && p[i] != '\"' && i < sizeof(cipher)-1) { cipher[i]=p[i]; i++; }
|
||||
cipher[i]='\0';
|
||||
}
|
||||
snprintf(req, sizeof(req), "{\"id\":\"6\",\"method\":\"nip04_decrypt\",\"params\":[\"%s\",\"%s\"]}", pub_a, cipher);
|
||||
snprintf(req, sizeof(req), "{\"id\":\"6\",\"method\":\"nostr_nip04_decrypt\",\"params\":[\"%s\",\"%s\"]}", pub_a, cipher);
|
||||
free(resp_a); resp_a = NULL;
|
||||
if (request_roundtrip_to(SOCKET_NAME_A, req, &resp_a) == 0) {
|
||||
check_condition("nip04 round-trip plaintext recovered", strstr(resp_a, "hello_nip04") != NULL);
|
||||
@@ -1074,7 +1075,7 @@ int main(void) {
|
||||
}
|
||||
|
||||
memset(cipher, 0, sizeof(cipher));
|
||||
snprintf(req, sizeof(req), "{\"id\":\"7\",\"method\":\"nip44_encrypt\",\"params\":[\"%s\",\"hello_nip44\"]}", pub_a);
|
||||
snprintf(req, sizeof(req), "{\"id\":\"7\",\"method\":\"nostr_nip44_encrypt\",\"params\":[\"%s\",\"hello_nip44\"]}", pub_a);
|
||||
free(resp_a); resp_a = NULL;
|
||||
if (request_roundtrip_to(SOCKET_NAME_A, req, &resp_a) == 0) {
|
||||
p = strstr(resp_a, "\"result\":\"");
|
||||
@@ -1084,7 +1085,7 @@ int main(void) {
|
||||
while (p[i] && p[i] != '\"' && i < sizeof(cipher)-1) { cipher[i]=p[i]; i++; }
|
||||
cipher[i]='\0';
|
||||
}
|
||||
snprintf(req, sizeof(req), "{\"id\":\"8\",\"method\":\"nip44_decrypt\",\"params\":[\"%s\",\"%s\"]}", pub_a, cipher);
|
||||
snprintf(req, sizeof(req), "{\"id\":\"8\",\"method\":\"nostr_nip44_decrypt\",\"params\":[\"%s\",\"%s\"]}", pub_a, cipher);
|
||||
free(resp_a); resp_a = NULL;
|
||||
if (request_roundtrip_to(SOCKET_NAME_A, req, &resp_a) == 0) {
|
||||
check_condition("nip44 round-trip plaintext recovered", strstr(resp_a, "hello_nip44") != NULL);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Test for mine_event verb (NIP-13 Proof-of-Work) */
|
||||
/* Test for nostr_mine_event verb (NIP-13 Proof-of-Work) */
|
||||
/* NSIGNER_HEADERLESS_DECLS_BEGIN */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -126,18 +126,14 @@ const char *selector_strerror(int err);
|
||||
#define ENFORCE_ERR_CURVE -2
|
||||
#define ENFORCE_ERR_UNKNOWN_VERB -3
|
||||
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_MINE_EVENT "mine_event"
|
||||
#define VERB_SIGN_DATA "sign_data"
|
||||
#define VERB_VERIFY_SIG "verify_signature"
|
||||
#define VERB_SSH_SIGN "ssh_sign"
|
||||
#define VERB_KEM_ENCAPS "kem_encapsulate"
|
||||
#define VERB_KEM_DECAPS "kem_decapsulate"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_MINE_EVENT "nostr_mine_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
int enforce_verb_role(const char *verb, const role_entry_t *role);
|
||||
const char *enforce_strerror(int err);
|
||||
@@ -497,7 +493,7 @@ static int response_has(const char *response, const char *needle) {
|
||||
return (response != NULL && needle != NULL && strstr(response, needle) != NULL);
|
||||
}
|
||||
|
||||
/* Parse a dispatcher response and extract the result object (for mine_event).
|
||||
/* Parse a dispatcher response and extract the result object (for nostr_mine_event).
|
||||
* Returns a newly-allocated cJSON result object, or NULL on failure.
|
||||
* Caller must delete the returned object. */
|
||||
static cJSON *extract_result_object(const char *response) {
|
||||
@@ -580,67 +576,67 @@ int main(void) {
|
||||
derived = crypto_derive_all(&key_store, &table, &mnemonic);
|
||||
check_condition("crypto_derive_all derives at least one key", derived >= 1);
|
||||
|
||||
/* 1. mine_event with low difficulty (2) and short timeout (5 sec) — should reach target */
|
||||
/* 1. nostr_mine_event with low difficulty (2) and short timeout (5 sec) — should reach target */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"1\",\"method\":\"mine_event\",\"params\":["
|
||||
"{\"id\":\"1\",\"method\":\"nostr_mine_event\",\"params\":["
|
||||
"\"{\\\"kind\\\":1,\\\"content\\\":\\\"hello pow\\\",\\\"tags\\\":[]}\","
|
||||
"{\"difficulty\":2,\"threads\":2,\"timeout_sec\":5}]}");
|
||||
check_condition("mine_event low difficulty returns result object",
|
||||
check_condition("nostr_mine_event low difficulty returns result object",
|
||||
response_has(resp, "\"id\":\"1\"") && response_has(resp, "\"result\"") && response_has(resp, "achieved_difficulty"));
|
||||
check_condition("mine_event low difficulty returns signed event with nonce tag",
|
||||
check_condition("nostr_mine_event low difficulty returns signed event with nonce tag",
|
||||
response_has(resp, "nonce") && response_has(resp, "sig") && response_has(resp, "pubkey"));
|
||||
check_condition("mine_event low difficulty target_reached is true",
|
||||
check_condition("nostr_mine_event low difficulty target_reached is true",
|
||||
check_target_reached(resp, 1));
|
||||
free(resp);
|
||||
|
||||
/* 2. mine_event with high difficulty (30) and short timeout (2 sec) — should NOT reach target */
|
||||
/* 2. nostr_mine_event with high difficulty (30) and short timeout (2 sec) — should NOT reach target */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"2\",\"method\":\"mine_event\",\"params\":["
|
||||
"{\"id\":\"2\",\"method\":\"nostr_mine_event\",\"params\":["
|
||||
"\"{\\\"kind\\\":1,\\\"content\\\":\\\"hard pow\\\",\\\"tags\\\":[]}\","
|
||||
"{\"difficulty\":30,\"threads\":4,\"timeout_sec\":2}]}");
|
||||
check_condition("mine_event high difficulty returns result object",
|
||||
check_condition("nostr_mine_event high difficulty returns result object",
|
||||
response_has(resp, "\"id\":\"2\"") && response_has(resp, "\"result\"") && response_has(resp, "achieved_difficulty"));
|
||||
check_condition("mine_event high difficulty target_reached is false",
|
||||
check_condition("nostr_mine_event high difficulty target_reached is false",
|
||||
check_target_reached(resp, 0));
|
||||
check_condition("mine_event high difficulty still returns a signed event",
|
||||
check_condition("nostr_mine_event high difficulty still returns a signed event",
|
||||
response_has(resp, "nonce") && response_has(resp, "sig"));
|
||||
free(resp);
|
||||
|
||||
/* 3. mine_event with only timeout (no difficulty) — should mine for full duration */
|
||||
/* 3. nostr_mine_event with only timeout (no difficulty) — should mine for full duration */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"3\",\"method\":\"mine_event\",\"params\":["
|
||||
"{\"id\":\"3\",\"method\":\"nostr_mine_event\",\"params\":["
|
||||
"\"{\\\"kind\\\":1,\\\"content\\\":\\\"timeout only\\\",\\\"tags\\\":[]}\","
|
||||
"{\"threads\":2,\"timeout_sec\":2}]}");
|
||||
check_condition("mine_event timeout-only returns result",
|
||||
check_condition("nostr_mine_event timeout-only returns result",
|
||||
response_has(resp, "\"id\":\"3\"") && response_has(resp, "\"result\"") && response_has(resp, "achieved_difficulty"));
|
||||
check_condition("mine_event timeout-only target_reached is false (no target set)",
|
||||
check_condition("nostr_mine_event timeout-only target_reached is false (no target set)",
|
||||
check_target_reached(resp, 0));
|
||||
free(resp);
|
||||
|
||||
/* 4. mine_event with neither difficulty nor timeout — should error */
|
||||
/* 4. nostr_mine_event with neither difficulty nor timeout — should error */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"4\",\"method\":\"mine_event\",\"params\":["
|
||||
"{\"id\":\"4\",\"method\":\"nostr_mine_event\",\"params\":["
|
||||
"\"{\\\"kind\\\":1,\\\"content\\\":\\\"no params\\\",\\\"tags\\\":[]}\","
|
||||
"{\"threads\":2}]}");
|
||||
check_condition("mine_event with no termination condition returns 1007",
|
||||
check_condition("nostr_mine_event with no termination condition returns 1007",
|
||||
response_has(resp, "\"id\":\"4\"") && response_has(resp, "\"code\":1007") && response_has(resp, "no_termination_condition"));
|
||||
free(resp);
|
||||
|
||||
/* 5. mine_event with only difficulty (no timeout) — should use safety timeout and reach low target */
|
||||
/* 5. nostr_mine_event with only difficulty (no timeout) — should use safety timeout and reach low target */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"5\",\"method\":\"mine_event\",\"params\":["
|
||||
"{\"id\":\"5\",\"method\":\"nostr_mine_event\",\"params\":["
|
||||
"\"{\\\"kind\\\":1,\\\"content\\\":\\\"difficulty only\\\",\\\"tags\\\":[]}\","
|
||||
"{\"difficulty\":1,\"threads\":2}]}");
|
||||
check_condition("mine_event difficulty-only returns result and reaches target",
|
||||
check_condition("nostr_mine_event difficulty-only returns result and reaches target",
|
||||
response_has(resp, "\"id\":\"5\"") && response_has(resp, "\"result\"") && check_target_reached(resp, 1));
|
||||
free(resp);
|
||||
|
||||
/* 6. mine_event with invalid event JSON */
|
||||
/* 6. nostr_mine_event with invalid event JSON */
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"6\",\"method\":\"mine_event\",\"params\":["
|
||||
"{\"id\":\"6\",\"method\":\"nostr_mine_event\",\"params\":["
|
||||
"\"not valid json\","
|
||||
"{\"difficulty\":1,\"timeout_sec\":2}]}");
|
||||
check_condition("mine_event with invalid event returns error",
|
||||
check_condition("nostr_mine_event with invalid event returns error",
|
||||
response_has(resp, "\"id\":\"6\"") && (response_has(resp, "\"code\":1008") || response_has(resp, "\"code\":-32602")));
|
||||
free(resp);
|
||||
|
||||
@@ -651,7 +647,7 @@ int main(void) {
|
||||
int pow_diff;
|
||||
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"7\",\"method\":\"mine_event\",\"params\":["
|
||||
"{\"id\":\"7\",\"method\":\"nostr_mine_event\",\"params\":["
|
||||
"\"{\\\"kind\\\":1,\\\"content\\\":\\\"verify pow\\\",\\\"tags\\\":[]}\","
|
||||
"{\"difficulty\":4,\"threads\":4,\"timeout_sec\":5}]}");
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* - ML-DSA-65 sign/verify with wrong key: verify fails
|
||||
* - DRBG determinism: same seed -> same randombytes output
|
||||
* - Integration: derive ML-DSA-65 key from mnemonic via crypto_derive_one,
|
||||
* sign data via sign_data verb through dispatcher, verify signature
|
||||
* - Enforcement: sign_data allowed on pq-sig+ml-dsa-65
|
||||
* sign data via sign verb through dispatcher, verify signature
|
||||
* - Enforcement: sign allowed on pq-sig+ml-dsa-65
|
||||
*/
|
||||
/* NSIGNER_HEADERLESS_DECLS_BEGIN */
|
||||
#include <stddef.h>
|
||||
@@ -142,19 +142,21 @@ const char *selector_strerror(int err);
|
||||
#define ENFORCE_ERR_PURPOSE -1
|
||||
#define ENFORCE_ERR_CURVE -2
|
||||
#define ENFORCE_ERR_UNKNOWN_VERB -3
|
||||
#define ENFORCE_ERR_ALGORITHM -4
|
||||
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_MINE_EVENT "mine_event"
|
||||
#define VERB_SIGN_DATA "sign_data"
|
||||
#define VERB_VERIFY_SIG "verify_signature"
|
||||
#define VERB_SSH_SIGN "ssh_sign"
|
||||
#define VERB_KEM_ENCAPS "kem_encapsulate"
|
||||
#define VERB_KEM_DECAPS "kem_decapsulate"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_SIGN "sign"
|
||||
#define VERB_VERIFY "verify"
|
||||
#define VERB_ENCAPSULATE "encapsulate"
|
||||
#define VERB_DECAPSULATE "decapsulate"
|
||||
#define VERB_DERIVE_SHARED "derive_shared_secret"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_MINE_EVENT "nostr_mine_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
int enforce_verb_role(const char *verb, const role_entry_t *role);
|
||||
const char *enforce_strerror(int err);
|
||||
@@ -682,7 +684,7 @@ int main(void) {
|
||||
mnemonic_unload(&mnemonic_state);
|
||||
}
|
||||
|
||||
/* ---- Integration: sign_data via dispatcher ---- */
|
||||
/* ---- Integration: sign via dispatcher ---- */
|
||||
{
|
||||
role_table_t table;
|
||||
role_entry_t pq_role;
|
||||
@@ -705,16 +707,16 @@ int main(void) {
|
||||
|
||||
dispatcher_init(&dispatcher, &table, &mnemonic_state, &g_key_store, &g_alg_key_cache);
|
||||
|
||||
/* sign_data request */
|
||||
/* sign request */
|
||||
snprintf(request, sizeof(request),
|
||||
"{\"id\":\"test1\",\"method\":\"sign_data\",\"params\":[\"%s\",{\"role\":\"pq_main\"}]}",
|
||||
"{\"id\":\"test1\",\"method\":\"sign\",\"params\":[\"%s\",{\"algorithm\":\"ml-dsa-65\",\"index\":0}]}",
|
||||
msg_hex);
|
||||
resp = dispatcher_handle_request(&dispatcher, request);
|
||||
check_condition("sign_data via dispatcher returns result",
|
||||
check_condition("sign via dispatcher returns result",
|
||||
resp != NULL && response_has(resp, "\"result\""));
|
||||
check_condition("sign_data result contains signature",
|
||||
check_condition("sign result contains signature",
|
||||
resp != NULL && response_has(resp, "signature"));
|
||||
check_condition("sign_data result contains algorithm ml-dsa-65",
|
||||
check_condition("sign result contains algorithm ml-dsa-65",
|
||||
resp != NULL && response_has(resp, "ml-dsa-65"));
|
||||
free(resp);
|
||||
|
||||
@@ -722,7 +724,7 @@ int main(void) {
|
||||
mnemonic_unload(&mnemonic_state);
|
||||
}
|
||||
|
||||
/* ---- Integration: verify_signature roundtrip via dispatcher ---- */
|
||||
/* ---- Integration: verify roundtrip via dispatcher ---- */
|
||||
{
|
||||
role_table_t table;
|
||||
role_entry_t pq_role;
|
||||
@@ -749,10 +751,10 @@ int main(void) {
|
||||
|
||||
/* Sign */
|
||||
snprintf(sign_req, sizeof(sign_req),
|
||||
"{\"id\":\"s1\",\"method\":\"sign_data\",\"params\":[\"%s\",{\"role\":\"pq_main\"}]}",
|
||||
"{\"id\":\"s1\",\"method\":\"sign\",\"params\":[\"%s\",{\"algorithm\":\"ml-dsa-65\",\"index\":0}]}",
|
||||
msg_hex);
|
||||
sign_resp = dispatcher_handle_request(&dispatcher, sign_req);
|
||||
check_condition("verify roundtrip: sign_data succeeds", sign_resp != NULL);
|
||||
check_condition("verify roundtrip: sign succeeds", sign_resp != NULL);
|
||||
|
||||
/* Extract signature from result */
|
||||
if (sign_resp != NULL) {
|
||||
@@ -777,28 +779,28 @@ int main(void) {
|
||||
/* Verify */
|
||||
if (sig_str != NULL) {
|
||||
snprintf(verify_req, sizeof(verify_req),
|
||||
"{\"id\":\"v1\",\"method\":\"verify_signature\",\"params\":[\"%s\",\"%s\",{\"role\":\"pq_main\"}]}",
|
||||
"{\"id\":\"v1\",\"method\":\"verify\",\"params\":[\"%s\",\"%s\",{\"algorithm\":\"ml-dsa-65\",\"index\":0}]}",
|
||||
msg_hex, sig_str);
|
||||
verify_resp = dispatcher_handle_request(&dispatcher, verify_req);
|
||||
check_condition("verify_signature returns valid:true",
|
||||
check_condition("verify returns valid:true",
|
||||
verify_resp != NULL && response_has(verify_resp, "valid") &&
|
||||
response_has(verify_resp, "true"));
|
||||
free(verify_resp);
|
||||
|
||||
/* Verify with wrong message */
|
||||
snprintf(verify_req, sizeof(verify_req),
|
||||
"{\"id\":\"v2\",\"method\":\"verify_signature\",\"params\":[\"00ff\",\"%s\",{\"role\":\"pq_main\"}]}",
|
||||
"{\"id\":\"v2\",\"method\":\"verify\",\"params\":[\"00ff\",\"%s\",{\"algorithm\":\"ml-dsa-65\",\"index\":0}]}",
|
||||
sig_str);
|
||||
verify_resp = dispatcher_handle_request(&dispatcher, verify_req);
|
||||
check_condition("verify_signature wrong msg returns valid:false",
|
||||
check_condition("verify wrong msg returns valid:false",
|
||||
verify_resp != NULL && response_has(verify_resp, "valid") &&
|
||||
response_has(verify_resp, "false"));
|
||||
free(verify_resp);
|
||||
|
||||
free((void *)sig_str);
|
||||
} else {
|
||||
check_condition("verify_signature returns valid:true", 0);
|
||||
check_condition("verify_signature wrong msg returns valid:false", 0);
|
||||
check_condition("verify returns valid:true", 0);
|
||||
check_condition("verify wrong msg returns valid:false", 0);
|
||||
}
|
||||
|
||||
free(sign_resp);
|
||||
@@ -806,22 +808,26 @@ int main(void) {
|
||||
mnemonic_unload(&mnemonic_state);
|
||||
}
|
||||
|
||||
/* ---- Enforcement: sign_data on pq-sig+ml-dsa-65 ---- */
|
||||
/* ---- Enforcement: sign on pq-sig+ml-dsa-65 ---- */
|
||||
{
|
||||
role_entry_t pq_sig = make_pq_sig_ml_dsa_65_entry("pq", 0);
|
||||
role_entry_t nostr_secp = make_nostr_secp_entry("nostr", 0);
|
||||
|
||||
check_condition("enforce sign_data + pq-sig/ml-dsa-65 -> OK",
|
||||
enforce_verb_role(VERB_SIGN_DATA, &pq_sig) == ENFORCE_OK);
|
||||
/* sign/verify are algorithm-based now. */
|
||||
check_condition("enforce sign + ml-dsa-65 -> OK",
|
||||
enforce_verb_algorithm(VERB_SIGN, CRYPTO_ALG_ML_DSA_65) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce sign_data + nostr/secp256k1 -> PURPOSE err",
|
||||
enforce_verb_role(VERB_SIGN_DATA, &nostr_secp) == ENFORCE_ERR_PURPOSE);
|
||||
check_condition("enforce sign + secp256k1 -> OK",
|
||||
enforce_verb_algorithm(VERB_SIGN, CRYPTO_ALG_SECP256K1) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce verify_signature + pq-sig/ml-dsa-65 -> OK",
|
||||
enforce_verb_role(VERB_VERIFY_SIG, &pq_sig) == ENFORCE_OK);
|
||||
check_condition("enforce verify + ml-dsa-65 -> OK",
|
||||
enforce_verb_algorithm(VERB_VERIFY, CRYPTO_ALG_ML_DSA_65) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce sign_event + pq-sig/ml-dsa-65 -> PURPOSE err",
|
||||
enforce_verb_role(VERB_SIGN_EVENT, &pq_sig) == ENFORCE_ERR_PURPOSE);
|
||||
check_condition("enforce sign + ml-kem-768 -> ALGORITHM err",
|
||||
enforce_verb_algorithm(VERB_SIGN, CRYPTO_ALG_ML_KEM_768) == ENFORCE_ERR_ALGORITHM);
|
||||
|
||||
check_condition("enforce nostr_sign_event + pq-sig/ml-dsa-65 -> PURPOSE err",
|
||||
enforce_verb_role(VERB_NOSTR_SIGN_EVENT, &pq_sig) == ENFORCE_ERR_PURPOSE);
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* - Decaps with wrong ciphertext: different shared secret (implicit rejection)
|
||||
* - Integration: derive ML-KEM-768 key from mnemonic, encaps via dispatcher,
|
||||
* decaps via dispatcher, shared secrets match
|
||||
* - Enforcement: kem_encapsulate/kem_decapsulate allowed on pq-kem+ml-kem-768
|
||||
* - Enforcement: encapsulate/decapsulate allowed on pq-kem+ml-kem-768
|
||||
*/
|
||||
/* NSIGNER_HEADERLESS_DECLS_BEGIN */
|
||||
#include <stddef.h>
|
||||
@@ -112,19 +112,21 @@ const char *selector_strerror(int err);
|
||||
#define ENFORCE_ERR_PURPOSE -1
|
||||
#define ENFORCE_ERR_CURVE -2
|
||||
#define ENFORCE_ERR_UNKNOWN_VERB -3
|
||||
#define ENFORCE_ERR_ALGORITHM -4
|
||||
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_MINE_EVENT "mine_event"
|
||||
#define VERB_SIGN_DATA "sign_data"
|
||||
#define VERB_VERIFY_SIG "verify_signature"
|
||||
#define VERB_SSH_SIGN "ssh_sign"
|
||||
#define VERB_KEM_ENCAPS "kem_encapsulate"
|
||||
#define VERB_KEM_DECAPS "kem_decapsulate"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_SIGN "sign"
|
||||
#define VERB_VERIFY "verify"
|
||||
#define VERB_ENCAPSULATE "encapsulate"
|
||||
#define VERB_DECAPSULATE "decapsulate"
|
||||
#define VERB_DERIVE_SHARED "derive_shared_secret"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_MINE_EVENT "nostr_mine_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
int enforce_verb_role(const char *verb, const role_entry_t *role);
|
||||
const char *enforce_strerror(int err);
|
||||
@@ -521,7 +523,7 @@ int main(void) {
|
||||
mnemonic_unload(&mnemonic_state);
|
||||
}
|
||||
|
||||
/* ---- Integration: kem_encapsulate / kem_decapsulate via dispatcher ---- */
|
||||
/* ---- Integration: encapsulate / decapsulate via dispatcher ---- */
|
||||
{
|
||||
role_table_t table;
|
||||
role_entry_t pq_role;
|
||||
@@ -552,18 +554,18 @@ int main(void) {
|
||||
check_condition("dispatcher integration: pubkey available", pub_hex != NULL);
|
||||
|
||||
if (pub_hex != NULL && decaps_req != NULL) {
|
||||
/* kem_encapsulate request */
|
||||
/* encapsulate request */
|
||||
snprintf(encaps_req, sizeof(encaps_req),
|
||||
"{\"id\":\"e1\",\"method\":\"kem_encapsulate\",\"params\":[\"%s\",{\"role\":\"pq_kem\"}]}",
|
||||
"{\"id\":\"e1\",\"method\":\"encapsulate\",\"params\":[\"%s\",{\"algorithm\":\"ml-kem-768\",\"index\":0}]}",
|
||||
pub_hex);
|
||||
encaps_resp = dispatcher_handle_request(&dispatcher, encaps_req);
|
||||
check_condition("kem_encapsulate via dispatcher returns result",
|
||||
check_condition("encapsulate via dispatcher returns result",
|
||||
encaps_resp != NULL && response_has(encaps_resp, "\"result\""));
|
||||
check_condition("kem_encapsulate result contains ciphertext",
|
||||
check_condition("encapsulate result contains ciphertext",
|
||||
encaps_resp != NULL && response_has(encaps_resp, "ciphertext"));
|
||||
check_condition("kem_encapsulate result contains shared_secret",
|
||||
check_condition("encapsulate result contains shared_secret",
|
||||
encaps_resp != NULL && response_has(encaps_resp, "shared_secret"));
|
||||
check_condition("kem_encapsulate result contains algorithm ml-kem-768",
|
||||
check_condition("encapsulate result contains algorithm ml-kem-768",
|
||||
encaps_resp != NULL && response_has(encaps_resp, "ml-kem-768"));
|
||||
|
||||
/* Extract ciphertext and shared_secret from result */
|
||||
@@ -584,18 +586,18 @@ int main(void) {
|
||||
cJSON_Delete(encaps_json);
|
||||
}
|
||||
}
|
||||
check_condition("kem_encapsulate: extracted ciphertext hex", ct_str != NULL);
|
||||
check_condition("kem_encapsulate: extracted shared_secret hex", ss_encaps_str != NULL);
|
||||
check_condition("encapsulate: extracted ciphertext hex", ct_str != NULL);
|
||||
check_condition("encapsulate: extracted shared_secret hex", ss_encaps_str != NULL);
|
||||
|
||||
/* kem_decapsulate request */
|
||||
/* decapsulate request */
|
||||
if (ct_str != NULL) {
|
||||
snprintf(decaps_req, 6000,
|
||||
"{\"id\":\"d1\",\"method\":\"kem_decapsulate\",\"params\":[\"%s\",{\"role\":\"pq_kem\"}]}",
|
||||
"{\"id\":\"d1\",\"method\":\"decapsulate\",\"params\":[\"%s\",{\"algorithm\":\"ml-kem-768\",\"index\":0}]}",
|
||||
ct_str);
|
||||
decaps_resp = dispatcher_handle_request(&dispatcher, decaps_req);
|
||||
check_condition("kem_decapsulate via dispatcher returns result",
|
||||
check_condition("decapsulate via dispatcher returns result",
|
||||
decaps_resp != NULL && response_has(decaps_resp, "\"result\""));
|
||||
check_condition("kem_decapsulate result contains shared_secret",
|
||||
check_condition("decapsulate result contains shared_secret",
|
||||
decaps_resp != NULL && response_has(decaps_resp, "shared_secret"));
|
||||
|
||||
/* Extract shared_secret from decaps result */
|
||||
@@ -626,8 +628,8 @@ int main(void) {
|
||||
free((void *)ss_decaps_str);
|
||||
free(decaps_resp);
|
||||
} else {
|
||||
check_condition("kem_decapsulate via dispatcher returns result", 0);
|
||||
check_condition("kem_decapsulate result contains shared_secret", 0);
|
||||
check_condition("decapsulate via dispatcher returns result", 0);
|
||||
check_condition("decapsulate result contains shared_secret", 0);
|
||||
check_condition("dispatcher encaps/decaps shared secrets match", 0);
|
||||
}
|
||||
|
||||
@@ -641,22 +643,23 @@ int main(void) {
|
||||
mnemonic_unload(&mnemonic_state);
|
||||
}
|
||||
|
||||
/* ---- Enforcement: kem_encapsulate/kem_decapsulate on pq-kem+ml-kem-768 ---- */
|
||||
/* ---- Enforcement: encapsulate/decapsulate on pq-kem+ml-kem-768 ---- */
|
||||
{
|
||||
role_entry_t pq_kem = make_pq_kem_ml_kem_768_entry("pqk", 0);
|
||||
role_entry_t nostr_secp = make_nostr_secp_entry("nostr", 0);
|
||||
|
||||
check_condition("enforce kem_encapsulate + pq-kem/ml-kem-768 -> OK",
|
||||
enforce_verb_role(VERB_KEM_ENCAPS, &pq_kem) == ENFORCE_OK);
|
||||
/* encapsulate/decapsulate are algorithm-based now. */
|
||||
check_condition("enforce encapsulate + ml-kem-768 -> OK",
|
||||
enforce_verb_algorithm(VERB_ENCAPSULATE, CRYPTO_ALG_ML_KEM_768) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce kem_encapsulate + nostr/secp256k1 -> PURPOSE err",
|
||||
enforce_verb_role(VERB_KEM_ENCAPS, &nostr_secp) == ENFORCE_ERR_PURPOSE);
|
||||
check_condition("enforce encapsulate + secp256k1 -> ALGORITHM err",
|
||||
enforce_verb_algorithm(VERB_ENCAPSULATE, CRYPTO_ALG_SECP256K1) == ENFORCE_ERR_ALGORITHM);
|
||||
|
||||
check_condition("enforce kem_decapsulate + pq-kem/ml-kem-768 -> OK",
|
||||
enforce_verb_role(VERB_KEM_DECAPS, &pq_kem) == ENFORCE_OK);
|
||||
check_condition("enforce decapsulate + ml-kem-768 -> OK",
|
||||
enforce_verb_algorithm(VERB_DECAPSULATE, CRYPTO_ALG_ML_KEM_768) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce kem_decapsulate + nostr/secp256k1 -> PURPOSE err",
|
||||
enforce_verb_role(VERB_KEM_DECAPS, &nostr_secp) == ENFORCE_ERR_PURPOSE);
|
||||
check_condition("enforce decapsulate + ed25519 -> ALGORITHM err",
|
||||
enforce_verb_algorithm(VERB_DECAPSULATE, CRYPTO_ALG_ED25519) == ENFORCE_ERR_ALGORITHM);
|
||||
}
|
||||
|
||||
/* ---- Cleanup ---- */
|
||||
|
||||
@@ -220,19 +220,20 @@ const char *selector_strerror(int err);
|
||||
|
||||
|
||||
/* Known verbs */
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
/*
|
||||
* Check whether `verb` is allowed to execute against `role`.
|
||||
* Returns ENFORCE_OK if allowed, or an ENFORCE_ERR_* code.
|
||||
*
|
||||
* The enforcement rules are:
|
||||
* - All nostr verbs (sign_event, get_public_key, nip44_*, nip04_*) require:
|
||||
* - All nostr verbs (nostr_sign_event, nostr_get_public_key, nostr_nip44_*, nostr_nip04_*) require:
|
||||
* purpose == PURPOSE_NOSTR and curve == CURVE_SECP256K1
|
||||
* - Unknown verbs return ENFORCE_ERR_UNKNOWN_VERB (fail-closed).
|
||||
*/
|
||||
|
||||
@@ -220,19 +220,20 @@ const char *selector_strerror(int err);
|
||||
|
||||
|
||||
/* Known verbs */
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
/*
|
||||
* Check whether `verb` is allowed to execute against `role`.
|
||||
* Returns ENFORCE_OK if allowed, or an ENFORCE_ERR_* code.
|
||||
*
|
||||
* The enforcement rules are:
|
||||
* - All nostr verbs (sign_event, get_public_key, nip44_*, nip04_*) require:
|
||||
* - All nostr verbs (nostr_sign_event, nostr_get_public_key, nostr_nip44_*, nostr_nip04_*) require:
|
||||
* purpose == PURPOSE_NOSTR and curve == CURVE_SECP256K1
|
||||
* - Unknown verbs return ENFORCE_ERR_UNKNOWN_VERB (fail-closed).
|
||||
*/
|
||||
@@ -801,10 +802,10 @@ int main(void) {
|
||||
check_condition("parse_preapprove_spec rejects non-numeric nostr_index", rc == -1);
|
||||
|
||||
policy_init_default(&table, uid);
|
||||
rc = policy_check(&table, same_uid, "sign_event", "main", "nostr", NULL);
|
||||
rc = policy_check(&table, same_uid, "nostr_sign_event", "main", "nostr", NULL);
|
||||
check_condition("policy_init_default prompts same uid", rc == POLICY_PROMPT);
|
||||
|
||||
rc = policy_check(&table, other_uid, "sign_event", "main", "nostr", NULL);
|
||||
rc = policy_check(&table, other_uid, "nostr_sign_event", "main", "nostr", NULL);
|
||||
check_condition("policy_init_default prompts different uid", rc == POLICY_PROMPT);
|
||||
|
||||
/* Insert before catch-all: matching caller allowed, non-matching still prompted */
|
||||
@@ -820,9 +821,9 @@ int main(void) {
|
||||
rc = policy_table_insert_before_last(&table, &grant);
|
||||
check_condition("policy_table_insert_before_last succeeds", rc == 0);
|
||||
}
|
||||
rc = policy_check(&table, "uid:1000", "sign_event", "main", "nostr", NULL);
|
||||
rc = policy_check(&table, "uid:1000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
check_condition("insert_before_last matching caller returns POLICY_ALLOW", rc == POLICY_ALLOW);
|
||||
rc = policy_check(&table, "uid:2000", "sign_event", "main", "nostr", NULL);
|
||||
rc = policy_check(&table, "uid:2000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
check_condition("insert_before_last non-matching caller returns POLICY_PROMPT", rc == POLICY_PROMPT);
|
||||
|
||||
/* Preapprove parse + insert: matching caller allowed, non-matching prompted */
|
||||
@@ -831,70 +832,70 @@ int main(void) {
|
||||
check_condition("preapprove parse for policy insert succeeds", rc == 0);
|
||||
rc = policy_table_insert_before_last(&table, &parsed);
|
||||
check_condition("preapprove insert_before_last succeeds", rc == 0);
|
||||
rc = policy_check(&table, "uid:1000", "sign_event", "main", "nostr", NULL);
|
||||
rc = policy_check(&table, "uid:1000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
check_condition("preapprove allows matching caller+role", rc == POLICY_ALLOW);
|
||||
rc = policy_check(&table, "uid:2000", "sign_event", "main", "nostr", NULL);
|
||||
rc = policy_check(&table, "uid:2000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
check_condition("preapprove leaves non-matching caller at POLICY_PROMPT", rc == POLICY_PROMPT);
|
||||
|
||||
/* 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", NULL);
|
||||
add_single_entry(&table, "uid:1000", "nostr_sign_event", "main", "nostr", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:1000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
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", NULL);
|
||||
add_single_entry(&table, "*", "nostr_sign_event", "main", "nostr", PROMPT_DENY);
|
||||
rc = policy_check(&table, "uid:2000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
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", NULL);
|
||||
add_single_entry(&table, "uid:1000", "nostr_sign_event", "main", "nostr", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:9999", "nostr_sign_event", "main", "nostr", NULL);
|
||||
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", NULL);
|
||||
rc = policy_check(&table, "uid:1000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
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", NULL);
|
||||
add_single_entry(&table, "uid:1000", "nostr_sign_event", "throwaway", "nostr", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:1000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
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", NULL);
|
||||
add_single_entry(&table, "uid:1000", "nostr_sign_event", "main", "bitcoin", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:1000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
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", NULL);
|
||||
rc = policy_check(&table, "uid:1000", "nostr_nip44_encrypt", "main", "nostr", NULL);
|
||||
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", NULL);
|
||||
add_single_entry(&table, "uid:1000", "nostr_sign_event", "main", "nostr", PROMPT_FIRST_PER_BOOT);
|
||||
rc = policy_check(&table, "uid:1000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
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", NULL);
|
||||
add_single_entry(&table, "uid:1000", "nostr_sign_event", "main", "nostr", PROMPT_EVERY_REQUEST);
|
||||
rc = policy_check(&table, "uid:1000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
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", NULL);
|
||||
add_single_entry(&table, "uid:1000", "nostr_sign_event", "main", "nostr", PROMPT_DENY);
|
||||
add_single_entry(&table, "uid:1000", "nostr_sign_event", "main", "nostr", PROMPT_NEVER);
|
||||
rc = policy_check(&table, "uid:1000", "nostr_sign_event", "main", "nostr", NULL);
|
||||
check_condition("first matching entry wins", rc == POLICY_DENY);
|
||||
|
||||
/* Verify out_source for preapprove, session-grant, and default catch-all */
|
||||
@@ -931,15 +932,15 @@ int main(void) {
|
||||
check_condition("source test default catch-all add", rc == 0);
|
||||
|
||||
src = POLICY_SOURCE_DEFAULT;
|
||||
rc = policy_check(&table, "uid:1000", "sign_event", "main", "nostr", &src);
|
||||
rc = policy_check(&table, "uid:1000", "nostr_sign_event", "main", "nostr", &src);
|
||||
check_condition("policy_check reports preapprove source", rc == POLICY_ALLOW && src == POLICY_SOURCE_PREAPPROVE);
|
||||
|
||||
src = POLICY_SOURCE_DEFAULT;
|
||||
rc = policy_check(&table, "uid:1001", "sign_event", "main", "nostr", &src);
|
||||
rc = policy_check(&table, "uid:1001", "nostr_sign_event", "main", "nostr", &src);
|
||||
check_condition("policy_check reports session-grant source", rc == POLICY_ALLOW && src == POLICY_SOURCE_SESSION_GRANT);
|
||||
|
||||
src = POLICY_SOURCE_PREAPPROVE;
|
||||
rc = policy_check(&table, "uid:9999", "sign_event", "main", "nostr", &src);
|
||||
rc = policy_check(&table, "uid:9999", "nostr_sign_event", "main", "nostr", &src);
|
||||
check_condition("policy_check resets source for prompt/default", rc == POLICY_PROMPT && src == POLICY_SOURCE_DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
@@ -148,12 +148,13 @@ const char *selector_strerror(int err);
|
||||
#define ENFORCE_ERR_CURVE -2
|
||||
#define ENFORCE_ERR_UNKNOWN_VERB -3
|
||||
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
int enforce_verb_role(const char *verb, const role_entry_t *role);
|
||||
const char *enforce_strerror(int err);
|
||||
|
||||
@@ -145,18 +145,14 @@ const char *selector_strerror(int err);
|
||||
#define ENFORCE_ERR_CURVE -2
|
||||
#define ENFORCE_ERR_UNKNOWN_VERB -3
|
||||
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_MINE_EVENT "mine_event"
|
||||
#define VERB_SIGN_DATA "sign_data"
|
||||
#define VERB_VERIFY_SIG "verify_signature"
|
||||
#define VERB_SSH_SIGN "ssh_sign"
|
||||
#define VERB_KEM_ENCAPS "kem_encapsulate"
|
||||
#define VERB_KEM_DECAPS "kem_decapsulate"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_MINE_EVENT "nostr_mine_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
int enforce_verb_role(const char *verb, const role_entry_t *role);
|
||||
const char *enforce_strerror(int err);
|
||||
@@ -539,16 +535,23 @@ int main(void) {
|
||||
dispatcher_init(&dispatcher, &table, &mnemonic_state, &g_key_store, &g_alg_key_cache);
|
||||
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"1\",\"method\":\"get_public_key\",\"params\":[{\"role\":\"main\"}]}");
|
||||
"{\"id\":\"1\",\"method\":\"get_public_key\",\"params\":[{\"algorithm\":\"secp256k1\",\"index\":0}]}");
|
||||
check_condition("secp256k1 get_public_key returns a response", resp != NULL);
|
||||
|
||||
/* Algorithm-based get_public_key always returns a structured object. */
|
||||
pr = parse_result(resp, &result_str, &result_obj);
|
||||
check_condition("secp256k1 get_public_key result is a plain string (not object)",
|
||||
pr == 1 && result_str != NULL);
|
||||
check_condition("secp256k1 plain hex result is 64 hex chars",
|
||||
result_str != NULL && strlen(result_str) == 64);
|
||||
check_condition("secp256k1 plain hex result has no 'algorithm' field",
|
||||
!response_has(resp, "algorithm"));
|
||||
check_condition("secp256k1 get_public_key result is a JSON object",
|
||||
pr == 2 && result_obj != NULL);
|
||||
if (result_obj != NULL) {
|
||||
cJSON *pk_item = cJSON_GetObjectItemCaseSensitive(result_obj, "public_key");
|
||||
check_condition("secp256k1 structured has public_key (64 hex chars)",
|
||||
pk_item != NULL && cJSON_IsString(pk_item) &&
|
||||
strlen(pk_item->valuestring) == 64);
|
||||
} else {
|
||||
check_condition("secp256k1 structured has public_key (64 hex chars)", 0);
|
||||
}
|
||||
check_condition("secp256k1 structured has algorithm field",
|
||||
response_has(resp, "algorithm"));
|
||||
|
||||
free(result_str);
|
||||
cJSON_Delete(result_obj);
|
||||
@@ -584,7 +587,7 @@ int main(void) {
|
||||
dispatcher_init(&dispatcher, &table, &mnemonic_state, &g_key_store, &g_alg_key_cache);
|
||||
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"2\",\"method\":\"get_public_key\",\"params\":[{\"role\":\"main\",\"format\":\"structured\"}]}");
|
||||
"{\"id\":\"2\",\"method\":\"get_public_key\",\"params\":[{\"algorithm\":\"secp256k1\",\"index\":0,\"format\":\"structured\"}]}");
|
||||
check_condition("secp256k1 structured get_public_key returns a response", resp != NULL);
|
||||
|
||||
pr = parse_result(resp, &result_str, &result_obj);
|
||||
@@ -645,7 +648,7 @@ int main(void) {
|
||||
dispatcher_init(&dispatcher, &table, &mnemonic_state, &g_key_store, &g_alg_key_cache);
|
||||
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"3\",\"method\":\"get_public_key\",\"params\":[{\"role\":\"ssh_main\"}]}");
|
||||
"{\"id\":\"3\",\"method\":\"get_public_key\",\"params\":[{\"algorithm\":\"ed25519\",\"index\":0}]}");
|
||||
check_condition("ed25519 get_public_key returns a response", resp != NULL);
|
||||
|
||||
pr = parse_result(resp, &result_str, &result_obj);
|
||||
@@ -710,7 +713,7 @@ int main(void) {
|
||||
check_condition("ML-DSA-65 sizes available", sz != NULL);
|
||||
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"4\",\"method\":\"get_public_key\",\"params\":[{\"role\":\"pq_sig\"}]}");
|
||||
"{\"id\":\"4\",\"method\":\"get_public_key\",\"params\":[{\"algorithm\":\"ml-dsa-65\",\"index\":0}]}");
|
||||
check_condition("ML-DSA-65 get_public_key returns a response", resp != NULL);
|
||||
|
||||
pr = parse_result(resp, &result_str, &result_obj);
|
||||
@@ -775,7 +778,7 @@ int main(void) {
|
||||
check_condition("ML-KEM-768 sizes available", sz != NULL);
|
||||
|
||||
resp = dispatcher_handle_request(&dispatcher,
|
||||
"{\"id\":\"5\",\"method\":\"get_public_key\",\"params\":[{\"role\":\"kem_main\"}]}");
|
||||
"{\"id\":\"5\",\"method\":\"get_public_key\",\"params\":[{\"algorithm\":\"ml-kem-768\",\"index\":0}]}");
|
||||
check_condition("ML-KEM-768 get_public_key returns a response", resp != NULL);
|
||||
|
||||
pr = parse_result(resp, &result_str, &result_obj);
|
||||
|
||||
@@ -304,7 +304,7 @@ int main(void) {
|
||||
check_condition("nostr_init for auth signing", nostr_init() == 0);
|
||||
|
||||
/* no auth => legacy qubes:personal preapprove should match */
|
||||
if (run_qrexec_once("{\"id\":\"1\",\"method\":\"get_public_key\",\"params\":[\"\"]}", &resp) == 0) {
|
||||
if (run_qrexec_once("{\"id\":\"1\",\"method\":\"nostr_get_public_key\",\"params\":[\"\"]}", &resp) == 0) {
|
||||
check_condition("qrexec optional/no-auth still allows legacy qubes caller preapprove",
|
||||
strstr(resp, "\"result\":") != NULL);
|
||||
free(resp);
|
||||
@@ -313,7 +313,7 @@ int main(void) {
|
||||
check_condition("qrexec optional/no-auth request roundtrip", 0);
|
||||
}
|
||||
|
||||
auth_req = build_auth_request(privkey, "2", "get_public_key");
|
||||
auth_req = build_auth_request(privkey, "2", "nostr_get_public_key");
|
||||
check_condition("build qrexec auth request", auth_req != NULL);
|
||||
if (auth_req != NULL) {
|
||||
if (run_qrexec_once(auth_req, &resp) == 0) {
|
||||
@@ -328,7 +328,7 @@ int main(void) {
|
||||
auth_req = NULL;
|
||||
}
|
||||
|
||||
auth_req = build_auth_request(privkey, "3", "get_public_key");
|
||||
auth_req = build_auth_request(privkey, "3", "nostr_get_public_key");
|
||||
check_condition("build second qrexec auth request", auth_req != NULL);
|
||||
if (auth_req != NULL) {
|
||||
cJSON *root = cJSON_Parse(auth_req);
|
||||
@@ -338,7 +338,7 @@ int main(void) {
|
||||
if (root != NULL) {
|
||||
method_item = cJSON_GetObjectItemCaseSensitive(root, "method");
|
||||
if (cJSON_IsString(method_item)) {
|
||||
cJSON_SetValuestring(method_item, "sign_event");
|
||||
cJSON_SetValuestring(method_item, "nostr_sign_event");
|
||||
}
|
||||
tampered = cJSON_PrintUnformatted(root);
|
||||
cJSON_Delete(root);
|
||||
|
||||
@@ -223,19 +223,20 @@ const char *selector_strerror(int err);
|
||||
|
||||
|
||||
/* Known verbs */
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
/*
|
||||
* Check whether `verb` is allowed to execute against `role`.
|
||||
* Returns ENFORCE_OK if allowed, or an ENFORCE_ERR_* code.
|
||||
*
|
||||
* The enforcement rules are:
|
||||
* - All nostr verbs (sign_event, get_public_key, nip44_*, nip04_*) require:
|
||||
* - All nostr verbs (nostr_sign_event, nostr_get_public_key, nostr_nip44_*, nostr_nip04_*) require:
|
||||
* purpose == PURPOSE_NOSTR and curve == CURVE_SECP256K1
|
||||
* - Unknown verbs return ENFORCE_ERR_UNKNOWN_VERB (fail-closed).
|
||||
*/
|
||||
|
||||
@@ -220,19 +220,20 @@ const char *selector_strerror(int err);
|
||||
|
||||
|
||||
/* Known verbs */
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
/*
|
||||
* Check whether `verb` is allowed to execute against `role`.
|
||||
* Returns ENFORCE_OK if allowed, or an ENFORCE_ERR_* code.
|
||||
*
|
||||
* The enforcement rules are:
|
||||
* - All nostr verbs (sign_event, get_public_key, nip44_*, nip04_*) require:
|
||||
* - All nostr verbs (nostr_sign_event, nostr_get_public_key, nostr_nip44_*, nostr_nip04_*) require:
|
||||
* purpose == PURPOSE_NOSTR and curve == CURVE_SECP256K1
|
||||
* - Unknown verbs return ENFORCE_ERR_UNKNOWN_VERB (fail-closed).
|
||||
*/
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* - SLH-DSA-128s sign/verify with wrong key: verify fails
|
||||
* - DRBG determinism: same seed -> same randombytes output
|
||||
* - Integration: derive SLH-DSA-128s key from mnemonic via crypto_derive_one,
|
||||
* sign data via sign_data verb through dispatcher, verify signature
|
||||
* - Enforcement: sign_data allowed on pq-sig+slh-dsa-128s
|
||||
* sign data via sign verb through dispatcher, verify signature
|
||||
* - Enforcement: sign allowed on pq-sig+slh-dsa-128s
|
||||
*/
|
||||
/* NSIGNER_HEADERLESS_DECLS_BEGIN */
|
||||
#include <stddef.h>
|
||||
@@ -142,19 +142,21 @@ const char *selector_strerror(int err);
|
||||
#define ENFORCE_ERR_PURPOSE -1
|
||||
#define ENFORCE_ERR_CURVE -2
|
||||
#define ENFORCE_ERR_UNKNOWN_VERB -3
|
||||
#define ENFORCE_ERR_ALGORITHM -4
|
||||
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_MINE_EVENT "mine_event"
|
||||
#define VERB_SIGN_DATA "sign_data"
|
||||
#define VERB_VERIFY_SIG "verify_signature"
|
||||
#define VERB_SSH_SIGN "ssh_sign"
|
||||
#define VERB_KEM_ENCAPS "kem_encapsulate"
|
||||
#define VERB_KEM_DECAPS "kem_decapsulate"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_SIGN "sign"
|
||||
#define VERB_VERIFY "verify"
|
||||
#define VERB_ENCAPSULATE "encapsulate"
|
||||
#define VERB_DECAPSULATE "decapsulate"
|
||||
#define VERB_DERIVE_SHARED "derive_shared_secret"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_MINE_EVENT "nostr_mine_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
int enforce_verb_role(const char *verb, const role_entry_t *role);
|
||||
const char *enforce_strerror(int err);
|
||||
@@ -682,7 +684,7 @@ int main(void) {
|
||||
mnemonic_unload(&mnemonic_state);
|
||||
}
|
||||
|
||||
/* ---- Integration: sign_data via dispatcher ---- */
|
||||
/* ---- Integration: sign via dispatcher ---- */
|
||||
{
|
||||
role_table_t table;
|
||||
role_entry_t pq_role;
|
||||
@@ -705,16 +707,16 @@ int main(void) {
|
||||
|
||||
dispatcher_init(&dispatcher, &table, &mnemonic_state, &g_key_store, &g_alg_key_cache);
|
||||
|
||||
/* sign_data request */
|
||||
/* sign request */
|
||||
snprintf(request, sizeof(request),
|
||||
"{\"id\":\"test1\",\"method\":\"sign_data\",\"params\":[\"%s\",{\"role\":\"pq_main\"}]}",
|
||||
"{\"id\":\"test1\",\"method\":\"sign\",\"params\":[\"%s\",{\"algorithm\":\"slh-dsa-128s\",\"index\":0}]}",
|
||||
msg_hex);
|
||||
resp = dispatcher_handle_request(&dispatcher, request);
|
||||
check_condition("sign_data via dispatcher returns result",
|
||||
check_condition("sign via dispatcher returns result",
|
||||
resp != NULL && response_has(resp, "\"result\""));
|
||||
check_condition("sign_data result contains signature",
|
||||
check_condition("sign result contains signature",
|
||||
resp != NULL && response_has(resp, "signature"));
|
||||
check_condition("sign_data result contains algorithm slh-dsa-128s",
|
||||
check_condition("sign result contains algorithm slh-dsa-128s",
|
||||
resp != NULL && response_has(resp, "slh-dsa-128s"));
|
||||
free(resp);
|
||||
|
||||
@@ -722,7 +724,7 @@ int main(void) {
|
||||
mnemonic_unload(&mnemonic_state);
|
||||
}
|
||||
|
||||
/* ---- Integration: verify_signature roundtrip via dispatcher ---- */
|
||||
/* ---- Integration: verify roundtrip via dispatcher ---- */
|
||||
{
|
||||
role_table_t table;
|
||||
role_entry_t pq_role;
|
||||
@@ -752,10 +754,10 @@ int main(void) {
|
||||
|
||||
/* Sign */
|
||||
snprintf(sign_req, sizeof(sign_req),
|
||||
"{\"id\":\"s1\",\"method\":\"sign_data\",\"params\":[\"%s\",{\"role\":\"pq_main\"}]}",
|
||||
"{\"id\":\"s1\",\"method\":\"sign\",\"params\":[\"%s\",{\"algorithm\":\"slh-dsa-128s\",\"index\":0}]}",
|
||||
msg_hex);
|
||||
sign_resp = dispatcher_handle_request(&dispatcher, sign_req);
|
||||
check_condition("verify roundtrip: sign_data succeeds", sign_resp != NULL);
|
||||
check_condition("verify roundtrip: sign succeeds", sign_resp != NULL);
|
||||
|
||||
/* Extract signature from result */
|
||||
if (sign_resp != NULL) {
|
||||
@@ -780,28 +782,28 @@ int main(void) {
|
||||
/* Verify */
|
||||
if (sig_str != NULL && verify_req != NULL) {
|
||||
snprintf(verify_req, 20000,
|
||||
"{\"id\":\"v1\",\"method\":\"verify_signature\",\"params\":[\"%s\",\"%s\",{\"role\":\"pq_main\"}]}",
|
||||
"{\"id\":\"v1\",\"method\":\"verify\",\"params\":[\"%s\",\"%s\",{\"algorithm\":\"slh-dsa-128s\",\"index\":0}]}",
|
||||
msg_hex, sig_str);
|
||||
verify_resp = dispatcher_handle_request(&dispatcher, verify_req);
|
||||
check_condition("verify_signature returns valid:true",
|
||||
check_condition("verify returns valid:true",
|
||||
verify_resp != NULL && response_has(verify_resp, "valid") &&
|
||||
response_has(verify_resp, "true"));
|
||||
free(verify_resp);
|
||||
|
||||
/* Verify with wrong message */
|
||||
snprintf(verify_req, 20000,
|
||||
"{\"id\":\"v2\",\"method\":\"verify_signature\",\"params\":[\"00ff\",\"%s\",{\"role\":\"pq_main\"}]}",
|
||||
"{\"id\":\"v2\",\"method\":\"verify\",\"params\":[\"00ff\",\"%s\",{\"algorithm\":\"slh-dsa-128s\",\"index\":0}]}",
|
||||
sig_str);
|
||||
verify_resp = dispatcher_handle_request(&dispatcher, verify_req);
|
||||
check_condition("verify_signature wrong msg returns valid:false",
|
||||
check_condition("verify wrong msg returns valid:false",
|
||||
verify_resp != NULL && response_has(verify_resp, "valid") &&
|
||||
response_has(verify_resp, "false"));
|
||||
free(verify_resp);
|
||||
|
||||
free((void *)sig_str);
|
||||
} else {
|
||||
check_condition("verify_signature returns valid:true", 0);
|
||||
check_condition("verify_signature wrong msg returns valid:false", 0);
|
||||
check_condition("verify returns valid:true", 0);
|
||||
check_condition("verify wrong msg returns valid:false", 0);
|
||||
}
|
||||
|
||||
free(verify_req);
|
||||
@@ -810,19 +812,23 @@ int main(void) {
|
||||
mnemonic_unload(&mnemonic_state);
|
||||
}
|
||||
|
||||
/* ---- Enforcement: sign_data on pq-sig+slh-dsa-128s ---- */
|
||||
/* ---- Enforcement: sign on pq-sig+slh-dsa-128s ---- */
|
||||
{
|
||||
role_entry_t pq_sig = make_pq_sig_slh_dsa_128s_entry("pq", 0);
|
||||
role_entry_t nostr_secp = make_nostr_secp_entry("nostr", 0);
|
||||
|
||||
check_condition("enforce sign_data + pq-sig/slh-dsa-128s -> OK",
|
||||
enforce_verb_role(VERB_SIGN_DATA, &pq_sig) == ENFORCE_OK);
|
||||
/* sign/verify are algorithm-based now. */
|
||||
check_condition("enforce sign + slh-dsa-128s -> OK",
|
||||
enforce_verb_algorithm(VERB_SIGN, CRYPTO_ALG_SLH_DSA_128S) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce sign_data + nostr/secp256k1 -> PURPOSE err",
|
||||
enforce_verb_role(VERB_SIGN_DATA, &nostr_secp) == ENFORCE_ERR_PURPOSE);
|
||||
check_condition("enforce sign + secp256k1 -> OK",
|
||||
enforce_verb_algorithm(VERB_SIGN, CRYPTO_ALG_SECP256K1) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce verify_signature + pq-sig/slh-dsa-128s -> OK",
|
||||
enforce_verb_role(VERB_VERIFY_SIG, &pq_sig) == ENFORCE_OK);
|
||||
check_condition("enforce verify + slh-dsa-128s -> OK",
|
||||
enforce_verb_algorithm(VERB_VERIFY, CRYPTO_ALG_SLH_DSA_128S) == ENFORCE_OK);
|
||||
|
||||
check_condition("enforce sign + ml-kem-768 -> ALGORITHM err",
|
||||
enforce_verb_algorithm(VERB_SIGN, CRYPTO_ALG_ML_KEM_768) == ENFORCE_ERR_ALGORITHM);
|
||||
}
|
||||
|
||||
/* ---- Cleanup ---- */
|
||||
|
||||
@@ -220,19 +220,20 @@ const char *selector_strerror(int err);
|
||||
|
||||
|
||||
/* Known verbs */
|
||||
#define VERB_SIGN_EVENT "sign_event"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NIP44_ENCRYPT "nip44_encrypt"
|
||||
#define VERB_NIP44_DECRYPT "nip44_decrypt"
|
||||
#define VERB_NIP04_ENCRYPT "nip04_encrypt"
|
||||
#define VERB_NIP04_DECRYPT "nip04_decrypt"
|
||||
#define VERB_GET_PUBLIC_KEY "get_public_key"
|
||||
#define VERB_NOSTR_GET_PUBLIC_KEY "nostr_get_public_key"
|
||||
#define VERB_NOSTR_SIGN_EVENT "nostr_sign_event"
|
||||
#define VERB_NOSTR_NIP44_ENCRYPT "nostr_nip44_encrypt"
|
||||
#define VERB_NOSTR_NIP44_DECRYPT "nostr_nip44_decrypt"
|
||||
#define VERB_NOSTR_NIP04_ENCRYPT "nostr_nip04_encrypt"
|
||||
#define VERB_NOSTR_NIP04_DECRYPT "nostr_nip04_decrypt"
|
||||
|
||||
/*
|
||||
* Check whether `verb` is allowed to execute against `role`.
|
||||
* Returns ENFORCE_OK if allowed, or an ENFORCE_ERR_* code.
|
||||
*
|
||||
* The enforcement rules are:
|
||||
* - All nostr verbs (sign_event, get_public_key, nip44_*, nip04_*) require:
|
||||
* - All nostr verbs (nostr_sign_event, nostr_get_public_key, nostr_nip44_*, nostr_nip04_*) require:
|
||||
* purpose == PURPOSE_NOSTR and curve == CURVE_SECP256K1
|
||||
* - Unknown verbs return ENFORCE_ERR_UNKNOWN_VERB (fail-closed).
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user