upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/main/nostr_event.c
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-16 23:55:05 +0530
committerYour Name <you@example.com>2026-05-16 23:55:05 +0530
commit4c47ae188b288e7d24bd9566ab3e6a6805d9484f (patch)
tree33b74b2090b4f3b7597841734a56a4006a86d73f /main/nostr_event.c
parent133e40c82afb4d7659758b1fa57925ac57af4621 (diff)
Phase 3: Nostr identity derivation + wifistr service discovery
- Add identity.c/h: HMAC-SHA512 derivation from nsec → npub, STA/AP MAC, SSID, AP IP - Add nostr_event.c/h: NIP-01 event serialization + Schnorr signing (BIP-340) - Add geohash.c/h: lat/lon to geohash encoding - Add wifistr.c/h: kind 38787 event builder + WebSocket publish to Nostr relays - Update config.c/h: nsec-based identity, Nostr relay/geo config, remove static SSID/IP - Replace custom mbedTLS wallet with nucula library (libsecp256k1) - Remove wallet.c/h, wallet_persist.c/h (replaced by nucula_lib component) - Verified on Board A: derived SSID, captive portal, payment, wallet, wifistr publish
Diffstat (limited to 'main/nostr_event.c')
-rw-r--r--main/nostr_event.c112
1 files changed, 112 insertions, 0 deletions
diff --git a/main/nostr_event.c b/main/nostr_event.c
new file mode 100644
index 0000000..b55c47d
--- /dev/null
+++ b/main/nostr_event.c
@@ -0,0 +1,112 @@
1#include "nostr_event.h"
2#include "esp_log.h"
3#include "esp_err.h"
4#include "mbedtls/sha256.h"
5#include "secp256k1.h"
6#include "secp256k1_extrakeys.h"
7#include "secp256k1_schnorrsig.h"
8#include "cJSON.h"
9#include <string.h>
10#include <stdio.h>
11#include <sys/time.h>
12
13static const char *TAG = "nostr_event";
14
15static void bytes_to_hex(const uint8_t *bytes, size_t len, char *hex)
16{
17 for (size_t i = 0; i < len; i++)
18 sprintf(hex + i * 2, "%02x", bytes[i]);
19 hex[len * 2] = '\0';
20}
21
22esp_err_t nostr_event_init(nostr_event_t *event, const char *npub_hex,
23 int kind, const char *tags_json, const char *content)
24{
25 memset(event, 0, sizeof(*event));
26 strncpy(event->pubkey, npub_hex, sizeof(event->pubkey) - 1);
27 event->kind = kind;
28 event->tags_json = tags_json ? tags_json : "[]";
29 event->content = content ? content : "";
30
31 struct timeval tv;
32 gettimeofday(&tv, NULL);
33 event->created_at = (uint64_t)tv.tv_sec;
34
35 cJSON *serial = cJSON_CreateArray();
36 cJSON_AddItemToArray(serial, cJSON_CreateNumber(0));
37 cJSON_AddItemToArray(serial, cJSON_CreateString(event->pubkey));
38 cJSON_AddItemToArray(serial, cJSON_CreateNumber((double)event->created_at));
39 cJSON_AddItemToArray(serial, cJSON_CreateNumber(event->kind));
40 cJSON_AddItemToArray(serial, cJSON_Parse(event->tags_json));
41 cJSON_AddItemToArray(serial, cJSON_CreateString(event->content));
42
43 char *serialized = cJSON_PrintUnformatted(serial);
44 cJSON_Delete(serial);
45
46 uint8_t hash[32];
47 mbedtls_sha256((const unsigned char *)serialized, strlen(serialized),
48 hash, 0);
49 free(serialized);
50
51 bytes_to_hex(hash, 32, event->id);
52 return ESP_OK;
53}
54
55esp_err_t nostr_event_sign(nostr_event_t *event, const uint8_t nsec[32])
56{
57 uint8_t msg[32];
58 for (size_t i = 0; i < 32; i++) {
59 unsigned int byte;
60 sscanf(event->id + i * 2, "%02x", &byte);
61 msg[i] = (uint8_t)byte;
62 }
63
64 secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
65 if (!ctx) {
66 ESP_LOGE(TAG, "Failed to create secp256k1 context");
67 return ESP_ERR_NO_MEM;
68 }
69
70 secp256k1_keypair keypair;
71 if (!secp256k1_keypair_create(ctx, &keypair, nsec)) {
72 ESP_LOGE(TAG, "Invalid nsec for signing");
73 secp256k1_context_destroy(ctx);
74 return ESP_ERR_INVALID_ARG;
75 }
76
77 uint8_t sig[64];
78 if (!secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL)) {
79 ESP_LOGE(TAG, "Schnorr signing failed");
80 secp256k1_context_destroy(ctx);
81 return ESP_FAIL;
82 }
83
84 bytes_to_hex(sig, 64, event->sig);
85 secp256k1_context_destroy(ctx);
86 return ESP_OK;
87}
88
89esp_err_t nostr_event_to_json(const nostr_event_t *event, char *buf, size_t buf_len)
90{
91 cJSON *root = cJSON_CreateObject();
92 cJSON_AddStringToObject(root, "id", event->id);
93 cJSON_AddStringToObject(root, "pubkey", event->pubkey);
94 cJSON_AddNumberToObject(root, "created_at", (double)event->created_at);
95 cJSON_AddNumberToObject(root, "kind", event->kind);
96 cJSON_AddItemToObject(root, "tags", cJSON_Parse(event->tags_json));
97 cJSON_AddStringToObject(root, "content", event->content);
98 cJSON_AddStringToObject(root, "sig", event->sig);
99
100 char *json = cJSON_PrintUnformatted(root);
101 cJSON_Delete(root);
102
103 if (!json) return ESP_FAIL;
104 size_t len = strlen(json);
105 if (len >= buf_len) {
106 free(json);
107 return ESP_ERR_NO_MEM;
108 }
109 memcpy(buf, json, len + 1);
110 free(json);
111 return ESP_OK;
112}