Fix NIP-59 dynamic buffer sizing for NIP-17 gift wraps

This commit is contained in:
Laan Tungir
2026-04-11 15:53:44 -04:00
parent fc337cf921
commit af06f23939
3 changed files with 89 additions and 11 deletions
+1 -1
View File
@@ -1 +1 @@
0.5.15
0.5.16
+86 -8
View File
@@ -41,6 +41,39 @@ static time_t random_past_timestamp(long max_delay_sec) {
return now - random_offset;
}
/**
* NIP-44 padding calculation (mirrors nip044.c for output sizing)
*/
static size_t calc_nip44_padded_len(size_t unpadded_len) {
if (unpadded_len <= 32) {
return 32;
}
size_t next_power = 1;
while (next_power < unpadded_len) {
next_power <<= 1;
}
size_t chunk = (next_power <= 256) ? 32 : (next_power / 8);
return chunk * ((unpadded_len - 1) / chunk + 1);
}
/**
* Calculate safe output buffer size for NIP-44 encrypted base64 payload.
* Returns 0 if plaintext length exceeds NIP-44 max.
*/
static size_t calc_nip44_encrypted_b64_size(size_t plaintext_len) {
if (plaintext_len > NOSTR_NIP44_MAX_PLAINTEXT_SIZE) {
return 0;
}
size_t padded_len = calc_nip44_padded_len(plaintext_len) + 2; // +2 for length prefix
size_t payload_len = 1 + 32 + padded_len + 32; // version + nonce + ciphertext + mac
size_t b64_len = ((payload_len + 2) / 3) * 4 + 1; // +1 for NUL terminator
return b64_len;
}
/**
* Generate a random private key for gift wrap
*/
@@ -160,18 +193,31 @@ cJSON* nostr_nip59_create_seal(cJSON* rumor, const unsigned char* sender_private
}
// Encrypt the rumor using NIP-44
char encrypted_content[4096]; // Should be large enough for most events
size_t encrypted_size = calc_nip44_encrypted_b64_size(strlen(rumor_json));
if (encrypted_size == 0) {
free(rumor_json);
return NULL;
}
char* encrypted_content = malloc(encrypted_size);
if (!encrypted_content) {
free(rumor_json);
return NULL;
}
int encrypt_result = nostr_nip44_encrypt(sender_private_key, recipient_public_key,
rumor_json, encrypted_content, sizeof(encrypted_content));
rumor_json, encrypted_content, encrypted_size);
free(rumor_json);
if (encrypt_result != NOSTR_SUCCESS) {
free(encrypted_content);
return NULL;
}
// Get sender's public key
unsigned char sender_public_key[32];
if (nostr_ec_public_key_from_private_key(sender_private_key, sender_public_key) != 0) {
free(encrypted_content);
return NULL;
}
@@ -181,6 +227,7 @@ cJSON* nostr_nip59_create_seal(cJSON* rumor, const unsigned char* sender_private
// Create seal event (kind 13)
cJSON* seal = cJSON_CreateObject();
if (!seal) {
free(encrypted_content);
return NULL;
}
@@ -191,6 +238,7 @@ cJSON* nostr_nip59_create_seal(cJSON* rumor, const unsigned char* sender_private
cJSON_AddNumberToObject(seal, "kind", 13);
cJSON_AddItemToObject(seal, "tags", cJSON_CreateArray()); // Empty tags array
cJSON_AddStringToObject(seal, "content", encrypted_content);
free(encrypted_content);
// Calculate event ID
char event_id[65];
@@ -261,13 +309,27 @@ cJSON* nostr_nip59_create_gift_wrap(cJSON* seal, const char* recipient_public_ke
}
// Encrypt the seal using NIP-44
char encrypted_content[8192]; // Larger buffer for nested encryption
size_t encrypted_size = calc_nip44_encrypted_b64_size(strlen(seal_json));
if (encrypted_size == 0) {
memory_clear(random_private_key, 32);
free(seal_json);
return NULL;
}
char* encrypted_content = malloc(encrypted_size);
if (!encrypted_content) {
memory_clear(random_private_key, 32);
free(seal_json);
return NULL;
}
int encrypt_result = nostr_nip44_encrypt(random_private_key, recipient_public_key,
seal_json, encrypted_content, sizeof(encrypted_content));
seal_json, encrypted_content, encrypted_size);
free(seal_json);
if (encrypt_result != NOSTR_SUCCESS) {
memory_clear(random_private_key, 32);
free(encrypted_content);
return NULL;
}
@@ -275,6 +337,7 @@ cJSON* nostr_nip59_create_gift_wrap(cJSON* seal, const char* recipient_public_ke
cJSON* gift_wrap = cJSON_CreateObject();
if (!gift_wrap) {
memory_clear(random_private_key, 32);
free(encrypted_content);
return NULL;
}
@@ -293,6 +356,7 @@ cJSON* nostr_nip59_create_gift_wrap(cJSON* seal, const char* recipient_public_ke
cJSON_AddItemToObject(gift_wrap, "tags", tags);
cJSON_AddStringToObject(gift_wrap, "content", encrypted_content);
free(encrypted_content);
// Calculate event ID
char event_id[65];
@@ -359,16 +423,23 @@ cJSON* nostr_nip59_unwrap_gift(cJSON* gift_wrap, const unsigned char* recipient_
}
// Decrypt the content using NIP-44
char decrypted_json[8192];
size_t decrypted_size = strlen(encrypted_content) + 1;
char* decrypted_json = malloc(decrypted_size);
if (!decrypted_json) {
return NULL;
}
int decrypt_result = nostr_nip44_decrypt(recipient_private_key, sender_public_key,
encrypted_content, decrypted_json, sizeof(decrypted_json));
encrypted_content, decrypted_json, decrypted_size);
if (decrypt_result != NOSTR_SUCCESS) {
free(decrypted_json);
return NULL;
}
// Parse the decrypted JSON as the seal event
cJSON* seal = cJSON_Parse(decrypted_json);
free(decrypted_json);
if (!seal) {
return NULL;
}
@@ -394,16 +465,23 @@ cJSON* nostr_nip59_unseal_rumor(cJSON* seal, const unsigned char* sender_public_
const char* encrypted_content = cJSON_GetStringValue(content_item);
// Decrypt the content using NIP-44
char decrypted_json[4096];
size_t decrypted_size = strlen(encrypted_content) + 1;
char* decrypted_json = malloc(decrypted_size);
if (!decrypted_json) {
return NULL;
}
int decrypt_result = nostr_nip44_decrypt(recipient_private_key, sender_public_key,
encrypted_content, decrypted_json, sizeof(decrypted_json));
encrypted_content, decrypted_json, decrypted_size);
if (decrypt_result != NOSTR_SUCCESS) {
free(decrypted_json);
return NULL;
}
// Parse the decrypted JSON as the rumor event
cJSON* rumor = cJSON_Parse(decrypted_json);
free(decrypted_json);
if (!rumor) {
return NULL;
}
+2 -2
View File
@@ -2,10 +2,10 @@
#define NOSTR_CORE_H
// Version information (auto-updated by increment_and_push.sh)
#define VERSION "v0.5.15"
#define VERSION "v0.5.16"
#define VERSION_MAJOR 0
#define VERSION_MINOR 5
#define VERSION_PATCH 15
#define VERSION_PATCH 16
/*
* NOSTR Core Library - Complete API Reference