diff --git a/.gitignore b/.gitignore index f50c64c7..5257b230 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ nips/ node_modules/ nostr-tools/ tiny-AES-c/ - +blossom/ Trash/debug_tests/ diff --git a/README.md b/README.md index 63036747..29d3f823 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,118 @@ # NOSTR Core Library -A comprehensive, production-ready C library for NOSTR protocol implementation with OpenSSL-based cryptography and extensive protocol support. +A C library for NOSTR protocol implementation. Work in progress. -[![Version](https://img.shields.io/badge/version-0.1.20-blue.svg)](VERSION) +[![Version](https://img.shields.io/badge/version-0.2.1-blue.svg)](VERSION) [![License](https://img.shields.io/badge/license-MIT-green.svg)](#license) [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)](#building) -## ๐Ÿš€ Features -### Core Protocol Support -- **NIP-01**: Basic protocol flow - event creation, signing, and validation -- **NIP-04**: Encrypted direct messages (ECDH + AES-CBC + Base64) -- **NIP-05**: DNS-based internet identifier verification -- **NIP-06**: Key derivation from mnemonic (BIP39/BIP32 compliant) -- **NIP-11**: Relay information documents -- **NIP-13**: Proof of Work for events -- **NIP-44**: Versioned encrypted direct messages (ECDH + ChaCha20 + HMAC) +## ๐Ÿ“‹ NIP Implementation Status -### Cryptographic Features -- **OpenSSL-Based**: Production-grade cryptography with OpenSSL backend -- **Secp256k1**: Complete elliptic curve implementation bundled -- **BIP39**: Mnemonic phrase generation and validation -- **BIP32**: Hierarchical deterministic key derivation -- **ChaCha20**: Stream cipher for NIP-44 encryption -- **AES-CBC**: Block cipher for NIP-04 encryption -- **Schnorr Signatures**: BIP-340 compliant signing and verification +### Core Protocol NIPs +- [x] [NIP-01](nips/01.md) - Basic protocol flow - event creation, signing, and validation +- [ ] [NIP-02](nips/02.md) - Contact List and Petnames +- [ ] [NIP-03](nips/03.md) - OpenTimestamps Attestations for Events +- [x] [NIP-04](nips/04.md) - Encrypted Direct Messages (legacy) +- [x] [NIP-05](nips/05.md) - Mapping Nostr keys to DNS-based internet identifiers +- [x] [NIP-06](nips/06.md) - Basic key derivation from mnemonic seed phrase +- [ ] [NIP-07](nips/07.md) - `window.nostr` capability for web browsers +- [ ] [NIP-08](nips/08.md) - Handling Mentions +- [ ] [NIP-09](nips/09.md) - Event Deletion +- [ ] [NIP-10](nips/10.md) - Conventions for clients' use of `e` and `p` tags in text events +- [x] [NIP-11](nips/11.md) - Relay Information Document +- [ ] [NIP-12](nips/12.md) - Generic Tag Queries +- [x] [NIP-13](nips/13.md) - Proof of Work +- [ ] [NIP-14](nips/14.md) - Subject tag in text events +- [ ] [NIP-15](nips/15.md) - Nostr Marketplace (for resilient marketplaces) +- [ ] [NIP-16](nips/16.md) - Event Treatment +- [ ] [NIP-17](nips/17.md) - Private Direct Messages +- [ ] [NIP-18](nips/18.md) - Reposts +- [x] [NIP-19](nips/19.md) - bech32-encoded entities +- [ ] [NIP-20](nips/20.md) - Command Results +- [ ] [NIP-21](nips/21.md) - `nostr:` URI scheme +- [ ] [NIP-22](nips/22.md) - Event `created_at` Limits +- [ ] [NIP-23](nips/23.md) - Long-form Content +- [ ] [NIP-24](nips/24.md) - Extra metadata fields and tags +- [ ] [NIP-25](nips/25.md) - Reactions +- [ ] [NIP-26](nips/26.md) - Delegated Event Signing +- [ ] [NIP-27](nips/27.md) - Text Note References +- [ ] [NIP-28](nips/28.md) - Public Chat +- [ ] [NIP-29](nips/29.md) - Relay-based Groups +- [ ] [NIP-30](nips/30.md) - Custom Emoji +- [ ] [NIP-31](nips/31.md) - Dealing with Unknown Events +- [ ] [NIP-32](nips/32.md) - Labeling +- [ ] [NIP-33](nips/33.md) - Parameterized Replaceable Events +- [ ] [NIP-34](nips/34.md) - `git` stuff +- [ ] [NIP-35](nips/35.md) - Torrents +- [ ] [NIP-36](nips/36.md) - Sensitive Content / Content Warning +- [ ] [NIP-37](nips/37.md) - Draft Events +- [ ] [NIP-38](nips/38.md) - User Statuses +- [ ] [NIP-39](nips/39.md) - External Identities in Profiles +- [ ] [NIP-40](nips/40.md) - Expiration Timestamp +- [ ] [NIP-42](nips/42.md) - Authentication of clients to relays +- [x] [NIP-44](nips/44.md) - Versioned Encryption +- [ ] [NIP-45](nips/45.md) - Counting results +- [ ] [NIP-46](nips/46.md) - Nostr Connect +- [ ] [NIP-47](nips/47.md) - Wallet Connect +- [ ] [NIP-48](nips/48.md) - Proxy Tags +- [ ] [NIP-49](nips/49.md) - Private Key Encryption +- [ ] [NIP-50](nips/50.md) - Search Capability +- [ ] [NIP-51](nips/51.md) - Lists +- [ ] [NIP-52](nips/52.md) - Calendar Events +- [ ] [NIP-53](nips/53.md) - Live Activities +- [ ] [NIP-54](nips/54.md) - Wiki +- [ ] [NIP-55](nips/55.md) - Android Signer Application +- [ ] [NIP-56](nips/56.md) - Reporting +- [ ] [NIP-57](nips/57.md) - Lightning Zaps +- [ ] [NIP-58](nips/58.md) - Badges +- [ ] [NIP-59](nips/59.md) - Gift Wrap +- [ ] [NIP-60](nips/60.md) - Cashu Wallet +- [ ] [NIP-61](nips/61.md) - Nutzaps +- [ ] [NIP-62](nips/62.md) - Log events +- [ ] [NIP-64](nips/64.md) - Chess (PGN) +- [ ] [NIP-65](nips/65.md) - Relay List Metadata +- [ ] [NIP-66](nips/66.md) - Relay Monitor +- [ ] [NIP-68](nips/68.md) - Web badges +- [ ] [NIP-69](nips/69.md) - Peer-to-peer Order events +- [ ] [NIP-70](nips/70.md) - Protected Events +- [ ] [NIP-71](nips/71.md) - Video Events +- [ ] [NIP-72](nips/72.md) - Moderated Communities +- [ ] [NIP-73](nips/73.md) - External Content IDs +- [ ] [NIP-75](nips/75.md) - Zap Goals +- [ ] [NIP-77](nips/77.md) - Arbitrary custom app data +- [ ] [NIP-78](nips/78.md) - Application-specific data +- [ ] [NIP-84](nips/84.md) - Highlights +- [ ] [NIP-86](nips/86.md) - Relay Management API +- [ ] [NIP-87](nips/87.md) - Relay List Recommendations +- [ ] [NIP-88](nips/88.md) - Stella: A Stellar Relay +- [ ] [NIP-89](nips/89.md) - Recommended Application Handlers +- [ ] [NIP-90](nips/90.md) - Data Vending Machines +- [ ] [NIP-92](nips/92.md) - Media Attachments +- [ ] [NIP-94](nips/94.md) - File Metadata +- [ ] [NIP-96](nips/96.md) - HTTP File Storage Integration +- [ ] [NIP-98](nips/98.md) - HTTP Auth +- [ ] [NIP-99](nips/99.md) - Classified Listings -### Networking & Relay Support -- **Multi-Relay Queries**: Synchronous querying with progress callbacks -- **Relay Pools**: Asynchronous connection management with statistics -- **OpenSSL WebSocket Communication**: Full relay protocol support with TLS -- **NIP-05 Identifier Verification**: DNS-based identity resolution -- **NIP-11 Relay Information**: Automatic relay capability discovery -- **Event Deduplication**: Automatic handling of duplicate events across relays -- **Connection Management**: Automatic reconnection and error handling +**Legend:** โœ… Fully Implemented | โš ๏ธ Partial Implementation | โŒ Not Implemented -### Developer Experience -- **System Dependencies**: Uses system-installed OpenSSL, curl, and secp256k1 libraries -- **Thread-Safe**: Core cryptographic functions are stateless -- **Cross-Platform**: Builds on Linux, macOS, Windows -- **Comprehensive Examples**: Ready-to-run demonstration programs -- **Automatic Versioning**: Git-tag based version management +**Implementation Summary:** 8 of 96+ NIPs fully implemented (8.3%) + +## ๐Ÿ“ฆ Blossom Protocol Support (BUD Implementation Status) + +### Blossom Upgrade Documents (Client-Side Implementation) +- [ ] [BUD-01](blossom/buds/01.md) - Blob Retrieval - GET/HEAD endpoints for blob download +- [ ] [BUD-02](blossom/buds/02.md) - Upload/Management - Blob upload, listing, and deletion +- [ ] [BUD-03](blossom/buds/03.md) - User Server Lists - Managing preferred Blossom servers (kind 10063) +- [ ] [BUD-04](blossom/buds/04.md) - Mirroring - Copying blobs between servers +- [ ] [BUD-05](blossom/buds/05.md) - Media Optimization - Trusted server processing for media +- [ ] [BUD-06](blossom/buds/06.md) - Upload Requirements - Pre-upload validation with HEAD requests +- [ ] [BUD-07](blossom/buds/07.md) - Paid Operations - Payment handling (Cashu/Lightning) +- [ ] [BUD-08](blossom/buds/08.md) - NIP-94 Metadata - File metadata tag processing +- [ ] [BUD-09](blossom/buds/09.md) - Blob Reporting - Content moderation and reporting (NIP-56) + +**Implementation Summary:** 0 of 9 client-relevant BUDs implemented (0%) +**Protocol Overview:** [Blossom README](blossom/README.md) ## ๐Ÿ“ฆ Quick Start @@ -434,7 +506,7 @@ make arm64 ## ๐Ÿ“ˆ Version History -Current version: **0.1.20** +Current version: **0.2.1** The library uses automatic semantic versioning based on Git tags. Each build increments the patch version automatically. @@ -442,13 +514,15 @@ The library uses automatic semantic versioning based on Git tags. Each build inc - **OpenSSL Migration**: Transitioned from mbedTLS to OpenSSL for improved compatibility - **NIP-05 Support**: DNS-based internet identifier verification - **NIP-11 Support**: Relay information document fetching and parsing +- **NIP-19 Support**: Bech32-encoded entities (nsec/npub) - **Enhanced WebSocket**: OpenSSL-based TLS WebSocket communication -- **Production Ready**: Comprehensive test suite and error handling +- **Comprehensive Testing**: Extensive test suite and error handling **Version Timeline:** +- `v0.2.x` - Current development releases with enhanced NIP support - `v0.1.x` - Initial development releases - Focus on core protocol implementation and OpenSSL-based crypto -- Full NIP-01, NIP-04, NIP-05, NIP-06, NIP-11, NIP-13, NIP-44 support +- Full NIP-01, NIP-04, NIP-05, NIP-06, NIP-11, NIP-13, NIP-19, NIP-44 support ## ๐Ÿ› Troubleshooting @@ -496,4 +570,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file **Built with โค๏ธ for the decentralized web** -*OpenSSL-based โ€ข Minimal dependencies โ€ข Production ready* +*OpenSSL-based โ€ข Minimal dependencies โ€ข Work in progress* diff --git a/nostr_core/crypto/nostr_aes.c b/nostr_core/crypto/nostr_aes.c index d9972390..6b6e921b 100644 --- a/nostr_core/crypto/nostr_aes.c +++ b/nostr_core/crypto/nostr_aes.c @@ -1,15 +1,76 @@ /* * NOSTR AES Implementation - * + * * Based on tiny-AES-c by kokke (public domain) * Configured specifically for NIP-04: AES-256-CBC only - * + * * This is an implementation of the AES algorithm, specifically CBC mode. * Configured for AES-256 as required by NIP-04. */ +#include +#include #include // CBC mode, for memset -#include "nostr_aes.h" + +// Configure for NIP-04 requirements: AES-256-CBC only +#define CBC 1 +#define ECB 0 +#define CTR 0 + +// Configure for AES-256 (required by NIP-04) +#define AES128 0 +#define AES192 0 +#define AES256 1 + +#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only + +#if defined(AES256) && (AES256 == 1) + #define AES_KEYLEN 32 + #define AES_keyExpSize 240 +#elif defined(AES192) && (AES192 == 1) + #define AES_KEYLEN 24 + #define AES_keyExpSize 208 +#else + #define AES_KEYLEN 16 // Key length in bytes + #define AES_keyExpSize 176 +#endif + +struct AES_ctx +{ + uint8_t RoundKey[AES_keyExpSize]; +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) + uint8_t Iv[AES_BLOCKLEN]; +#endif +}; + +// state - array holding the intermediate results during decryption. +typedef uint8_t state_t[4][4]; + +// Function prototypes (internal use only) +static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key); +static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey); +static void SubBytes(state_t* state); +static void ShiftRows(state_t* state); +static uint8_t xtime(uint8_t x); +static void MixColumns(state_t* state); +static void InvMixColumns(state_t* state); +static void InvSubBytes(state_t* state); +static void InvShiftRows(state_t* state); +static void Cipher(state_t* state, const uint8_t* RoundKey); +static void InvCipher(state_t* state, const uint8_t* RoundKey); +static void XorWithIv(uint8_t* buf, const uint8_t* Iv); + +// Public functions (used by NIP-04 implementation) +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); +#endif + +#if defined(CBC) && (CBC == 1) +void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); +#endif // The number of columns comprising a state in AES. This is a constant in AES. Value=4 #define Nb 4 diff --git a/nostr_core/crypto/nostr_aes.h b/nostr_core/crypto/nostr_aes.h deleted file mode 100644 index 3b0aad18..00000000 --- a/nostr_core/crypto/nostr_aes.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef _NOSTR_AES_H_ -#define _NOSTR_AES_H_ - -#include -#include - -// Configure for NIP-04 requirements: AES-256-CBC only -#define CBC 1 -#define ECB 0 -#define CTR 0 - -// Configure for AES-256 (required by NIP-04) -#define AES128 0 -#define AES192 0 -#define AES256 1 - -#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only - -#if defined(AES256) && (AES256 == 1) - #define AES_KEYLEN 32 - #define AES_keyExpSize 240 -#elif defined(AES192) && (AES192 == 1) - #define AES_KEYLEN 24 - #define AES_keyExpSize 208 -#else - #define AES_KEYLEN 16 // Key length in bytes - #define AES_keyExpSize 176 -#endif - -struct AES_ctx -{ - uint8_t RoundKey[AES_keyExpSize]; -#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) - uint8_t Iv[AES_BLOCKLEN]; -#endif -}; - -void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); -#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) -void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); -void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); -#endif - -#if defined(CBC) && (CBC == 1) -// buffer size MUST be multiple of AES_BLOCKLEN; -// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme -// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() -// no IV should ever be reused with the same key -void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); -void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); -#endif // #if defined(CBC) && (CBC == 1) - -#endif // _NOSTR_AES_H_ diff --git a/nostr_core/crypto/nostr_chacha20.c b/nostr_core/crypto/nostr_chacha20.c index bb95d377..5814171c 100644 --- a/nostr_core/crypto/nostr_chacha20.c +++ b/nostr_core/crypto/nostr_chacha20.c @@ -1,15 +1,47 @@ /* * nostr_chacha20.c - ChaCha20 stream cipher implementation - * + * * Implementation based on RFC 8439 "ChaCha20 and Poly1305 for IETF Protocols" - * + * * This implementation is adapted from the RFC 8439 reference specification. * It prioritizes correctness and clarity over performance optimization. */ -#include "nostr_chacha20.h" +#include +#include #include +/* + * ============================================================================ + * CONSTANTS AND DEFINITIONS + * ============================================================================ + */ + +#define CHACHA20_KEY_SIZE 32 /* 256 bits */ +#define CHACHA20_NONCE_SIZE 12 /* 96 bits */ +#define CHACHA20_BLOCK_SIZE 64 /* 512 bits */ + +/* + * ============================================================================ + * FUNCTION PROTOTYPES (INTERNAL USE ONLY) + * ============================================================================ + */ + +// Internal utility functions +static uint32_t bytes_to_u32_le(const uint8_t *bytes); +static void u32_to_bytes_le(uint32_t val, uint8_t *bytes); + +// Public functions (used by NIP-44 implementation) +void chacha20_quarter_round(uint32_t state[16], int a, int b, int c, int d); +int chacha20_block(const uint8_t key[32], uint32_t counter, + const uint8_t nonce[12], uint8_t output[64]); +int chacha20_encrypt(const uint8_t key[32], uint32_t counter, + const uint8_t nonce[12], const uint8_t* input, + uint8_t* output, size_t length); +void chacha20_init_state(uint32_t state[16], const uint8_t key[32], + uint32_t counter, const uint8_t nonce[12]); +void chacha20_serialize_state(const uint32_t state[16], uint8_t output[64]); + /* * ============================================================================ * UTILITY MACROS AND FUNCTIONS diff --git a/nostr_core/crypto/nostr_chacha20.h b/nostr_core/crypto/nostr_chacha20.h deleted file mode 100644 index 93a2f35c..00000000 --- a/nostr_core/crypto/nostr_chacha20.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * nostr_chacha20.h - ChaCha20 stream cipher implementation - * - * Implementation based on RFC 8439 "ChaCha20 and Poly1305 for IETF Protocols" - * - * This is a small, portable implementation for NIP-44 support in the NOSTR library. - * The implementation prioritizes correctness and simplicity over performance. - */ - -#ifndef NOSTR_CHACHA20_H -#define NOSTR_CHACHA20_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * ============================================================================ - * CONSTANTS AND DEFINITIONS - * ============================================================================ - */ - -#define CHACHA20_KEY_SIZE 32 /* 256 bits */ -#define CHACHA20_NONCE_SIZE 12 /* 96 bits */ -#define CHACHA20_BLOCK_SIZE 64 /* 512 bits */ - -/* - * ============================================================================ - * CORE CHACHA20 FUNCTIONS - * ============================================================================ - */ - -/** - * ChaCha20 quarter round operation - * - * Operates on four 32-bit words performing the core ChaCha20 quarter round: - * a += b; d ^= a; d <<<= 16; - * c += d; b ^= c; b <<<= 12; - * a += b; d ^= a; d <<<= 8; - * c += d; b ^= c; b <<<= 7; - * - * @param state[in,out] ChaCha state as 16 32-bit words - * @param a, b, c, d Indices into state array for quarter round - */ -void chacha20_quarter_round(uint32_t state[16], int a, int b, int c, int d); - -/** - * ChaCha20 block function - * - * Transforms a 64-byte input block using ChaCha20 algorithm with 20 rounds. - * - * @param key[in] 32-byte key - * @param counter[in] 32-bit block counter - * @param nonce[in] 12-byte nonce - * @param output[out] 64-byte output buffer - * @return 0 on success, negative on error - */ -int chacha20_block(const uint8_t key[32], uint32_t counter, - const uint8_t nonce[12], uint8_t output[64]); - -/** - * ChaCha20 encryption/decryption - * - * Encrypts or decrypts data using ChaCha20 stream cipher. - * Since ChaCha20 is a stream cipher, encryption and decryption are the same operation. - * - * @param key[in] 32-byte key - * @param counter[in] Initial 32-bit counter value - * @param nonce[in] 12-byte nonce - * @param input[in] Input data to encrypt/decrypt - * @param output[out] Output buffer (can be same as input) - * @param length[in] Length of input data in bytes - * @return 0 on success, negative on error - */ -int chacha20_encrypt(const uint8_t key[32], uint32_t counter, - const uint8_t nonce[12], const uint8_t* input, - uint8_t* output, size_t length); - -/* - * ============================================================================ - * UTILITY FUNCTIONS - * ============================================================================ - */ - -/** - * Initialize ChaCha20 state matrix - * - * Sets up the initial 16-word state matrix with constants, key, counter, and nonce. - * - * @param state[out] 16-word state array to initialize - * @param key[in] 32-byte key - * @param counter[in] 32-bit block counter - * @param nonce[in] 12-byte nonce - */ -void chacha20_init_state(uint32_t state[16], const uint8_t key[32], - uint32_t counter, const uint8_t nonce[12]); - -/** - * Serialize ChaCha20 state to bytes - * - * Converts 16 32-bit words to 64 bytes in little-endian format. - * - * @param state[in] 16-word state array - * @param output[out] 64-byte output buffer - */ -void chacha20_serialize_state(const uint32_t state[16], uint8_t output[64]); - -#ifdef __cplusplus -} -#endif - -#endif /* NOSTR_CHACHA20_H */ diff --git a/nostr_core/crypto/nostr_secp256k1.c b/nostr_core/crypto/nostr_secp256k1.c index 8e2d1829..5556b98f 100644 --- a/nostr_core/crypto/nostr_secp256k1.c +++ b/nostr_core/crypto/nostr_secp256k1.c @@ -1,4 +1,3 @@ -#include "nostr_secp256k1.h" #include #include #include @@ -6,6 +5,33 @@ #include #include #include +#include + +/* + * PRIVATE INTERNAL FUNCTIONS - NOT EXPORTED + * These functions are for internal library use only. + */ + +/** Opaque data structure that holds a parsed and valid public key. + * Guaranteed to be 64 bytes in size, and can be safely copied/moved. + */ +typedef struct nostr_secp256k1_pubkey { + unsigned char data[64]; +} nostr_secp256k1_pubkey; + +/** Opaque data structure that holds a parsed keypair. + * Guaranteed to be 96 bytes in size, and can be safely copied/moved. + */ +typedef struct nostr_secp256k1_keypair { + unsigned char data[96]; +} nostr_secp256k1_keypair; + +/** Opaque data structure that holds a parsed x-only public key. + * Guaranteed to be 64 bytes in size, and can be safely copied/moved. + */ +typedef struct nostr_secp256k1_xonly_pubkey { + unsigned char data[64]; +} nostr_secp256k1_xonly_pubkey; // Global context for secp256k1 operations static secp256k1_context* g_ctx = NULL; diff --git a/nostr_core/crypto/nostr_secp256k1.h b/nostr_core/crypto/nostr_secp256k1.h deleted file mode 100644 index e5e3a46a..00000000 --- a/nostr_core/crypto/nostr_secp256k1.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef NOSTR_SECP256K1_H -#define NOSTR_SECP256K1_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/** Opaque data structure that holds a parsed and valid public key. - * Guaranteed to be 64 bytes in size, and can be safely copied/moved. - */ -typedef struct nostr_secp256k1_pubkey { - unsigned char data[64]; -} nostr_secp256k1_pubkey; - -/** Opaque data structure that holds a parsed keypair. - * Guaranteed to be 96 bytes in size, and can be safely copied/moved. - */ -typedef struct nostr_secp256k1_keypair { - unsigned char data[96]; -} nostr_secp256k1_keypair; - -/** Opaque data structure that holds a parsed x-only public key. - * Guaranteed to be 64 bytes in size, and can be safely copied/moved. - */ -typedef struct nostr_secp256k1_xonly_pubkey { - unsigned char data[64]; -} nostr_secp256k1_xonly_pubkey; - -/** Initialize the secp256k1 library. Must be called before any other functions. - * Returns: 1 on success, 0 on failure. - */ -int nostr_secp256k1_context_create(void); - -/** Clean up the secp256k1 library resources. - */ -void nostr_secp256k1_context_destroy(void); - -/** Verify an elliptic curve secret key. - * Returns: 1: secret key is valid, 0: secret key is invalid - * In: seckey: pointer to a 32-byte secret key. - */ -int nostr_secp256k1_ec_seckey_verify(const unsigned char *seckey); - -/** Compute the public key for a secret key. - * Returns: 1: secret was valid, public key stored. 0: secret was invalid. - * Out: pubkey: pointer to the created public key. - * In: seckey: pointer to a 32-byte secret key. - */ -int nostr_secp256k1_ec_pubkey_create(nostr_secp256k1_pubkey *pubkey, const unsigned char *seckey); - -/** Create a keypair from a secret key. - * Returns: 1: keypair created, 0: secret key invalid. - * Out: keypair: pointer to the created keypair. - * In: seckey: pointer to a 32-byte secret key. - */ -int nostr_secp256k1_keypair_create(nostr_secp256k1_keypair *keypair, const unsigned char *seckey); - -/** Get the x-only public key from a keypair. - * Returns: 1 always. - * Out: pubkey: pointer to storage for the x-only public key. - * In: keypair: pointer to a keypair. - */ -int nostr_secp256k1_keypair_xonly_pub(nostr_secp256k1_xonly_pubkey *pubkey, const nostr_secp256k1_keypair *keypair); - -/** Parse an x-only public key from bytes. - * Returns: 1: public key parsed, 0: invalid public key. - * Out: pubkey: pointer to the created x-only public key. - * In: input32: pointer to a 32-byte x-only public key. - */ -int nostr_secp256k1_xonly_pubkey_parse(nostr_secp256k1_xonly_pubkey *pubkey, const unsigned char *input32); - -/** Serialize an x-only public key to bytes. - * Returns: 1 always. - * Out: output32: pointer to a 32-byte array to store the serialized key. - * In: pubkey: pointer to an x-only public key. - */ -int nostr_secp256k1_xonly_pubkey_serialize(unsigned char *output32, const nostr_secp256k1_xonly_pubkey *pubkey); - -/** Create a Schnorr signature. - * Returns: 1: signature created, 0: nonce generation failed or secret key invalid. - * Out: sig64: pointer to a 64-byte array where the signature will be placed. - * In: msghash32: the 32-byte message hash being signed. - * keypair: pointer to an initialized keypair. - * aux_rand32: pointer to 32 bytes of auxiliary randomness (can be NULL). - */ -int nostr_secp256k1_schnorrsig_sign32(unsigned char *sig64, const unsigned char *msghash32, const nostr_secp256k1_keypair *keypair, const unsigned char *aux_rand32); - -/** Verify a Schnorr signature. - * Returns: 1: correct signature, 0: incorrect signature - * In: sig64: pointer to the 64-byte signature being verified. - * msghash32: the 32-byte message hash being verified. - * pubkey: pointer to an x-only public key to verify with. - */ -int nostr_secp256k1_schnorrsig_verify(const unsigned char *sig64, const unsigned char *msghash32, const nostr_secp256k1_xonly_pubkey *pubkey); - -/** Serialize a pubkey object into a serialized byte sequence. - * Returns: 1 always. - * Out: output: pointer to a 33-byte array to place the serialized key in. - * In: pubkey: pointer to a secp256k1_pubkey containing an initialized public key. - * - * The output will be a 33-byte compressed public key (0x02 or 0x03 prefix + 32 bytes x coordinate). - */ -int nostr_secp256k1_ec_pubkey_serialize_compressed(unsigned char *output, const nostr_secp256k1_pubkey *pubkey); - -/** Tweak a secret key by adding a 32-byte tweak to it. - * Returns: 1: seckey was valid, 0: seckey invalid or resulting key invalid - * In/Out: seckey: pointer to a 32-byte secret key. Will be modified in-place. - * In: tweak: pointer to a 32-byte tweak. - */ -int nostr_secp256k1_ec_seckey_tweak_add(unsigned char *seckey, const unsigned char *tweak); - -/** Parse a variable-length public key into the pubkey object. - * Returns: 1: public key parsed, 0: invalid public key. - * Out: pubkey: pointer to the created public key. - * In: input: pointer to a serialized public key - * inputlen: length of the array pointed to by input - */ -int nostr_secp256k1_ec_pubkey_parse(nostr_secp256k1_pubkey *pubkey, const unsigned char *input, size_t inputlen); - -/** Compute an EC Diffie-Hellman secret in constant time. - * Returns: 1: exponentiation was successful, 0: scalar was invalid (zero or overflow) - * Out: result: a 32-byte array which will be populated by an ECDH secret computed from point and scalar - * In: pubkey: a pointer to a secp256k1_pubkey containing an initialized public key - * seckey: a 32-byte scalar with which to multiply the point - */ -int nostr_secp256k1_ecdh(unsigned char *result, const nostr_secp256k1_pubkey *pubkey, const unsigned char *seckey, void *hashfp, void *data); - -/** Generate cryptographically secure random bytes. - * Returns: 1: success, 0: failure - * Out: buf: buffer to fill with random bytes - * In: len: number of bytes to generate - */ -int nostr_secp256k1_get_random_bytes(unsigned char *buf, size_t len); - -#ifdef __cplusplus -} -#endif - -#endif /* NOSTR_SECP256K1_H */ diff --git a/nostr_core/nip001.c b/nostr_core/nip001.c index 77402be9..69aa77f2 100644 --- a/nostr_core/nip001.c +++ b/nostr_core/nip001.c @@ -6,7 +6,6 @@ #include "nip001.h" #include "utils.h" -#include "crypto/nostr_secp256k1.h" #include "../cjson/cJSON.h" #include #include @@ -14,9 +13,21 @@ #include #include "../nostr_core/nostr_common.h" +// Forward declarations for crypto functions (private API) +// These functions are implemented in crypto/ but not exposed through public headers +typedef struct { + unsigned char data[64]; +} nostr_secp256k1_xonly_pubkey; + +int nostr_secp256k1_xonly_pubkey_parse(nostr_secp256k1_xonly_pubkey* pubkey, const unsigned char* input32); +int nostr_secp256k1_schnorrsig_verify(const unsigned char* sig64, const unsigned char* msg32, const nostr_secp256k1_xonly_pubkey* pubkey); + // Declare utility functions void nostr_bytes_to_hex(const unsigned char* bytes, size_t len, char* hex); int nostr_hex_to_bytes(const char* hex, unsigned char* bytes, size_t len); +int nostr_sha256(const unsigned char* data, size_t len, unsigned char* hash); +int nostr_ec_public_key_from_private_key(const unsigned char* private_key, unsigned char* public_key); +int nostr_ec_sign(const unsigned char* private_key, const unsigned char* hash, unsigned char* signature); /** * Create and sign a NOSTR event diff --git a/nostr_core/nip004.c b/nostr_core/nip004.c index 46144b7f..183f945e 100644 --- a/nostr_core/nip004.c +++ b/nostr_core/nip004.c @@ -6,13 +6,24 @@ #include "nip004.h" #include "utils.h" #include "nostr_common.h" -#include "crypto/nostr_secp256k1.h" #include #include #include -// Include our AES implementation -#include "crypto/nostr_aes.h" +// Forward declarations for crypto functions (private API) +// These functions are implemented in crypto/ but not exposed through public headers +int ecdh_shared_secret(const unsigned char* private_key, const unsigned char* public_key, unsigned char* shared_secret); +int nostr_secp256k1_get_random_bytes(unsigned char* buf, size_t len); + +// AES context and functions for NIP-04 encryption +struct AES_ctx { + unsigned char RoundKey[240]; // AES-256 key expansion + unsigned char Iv[16]; // Initialization vector +}; + +void AES_init_ctx_iv(struct AES_ctx* ctx, const unsigned char* key, const unsigned char* iv); +void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, unsigned char* buf, size_t length); +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, unsigned char* buf, size_t length); // Forward declarations for internal functions static int aes_cbc_encrypt(const unsigned char* key, const unsigned char* iv, diff --git a/nostr_core/nip044.c b/nostr_core/nip044.c index 3bf2a7aa..9051dfb7 100644 --- a/nostr_core/nip044.c +++ b/nostr_core/nip044.c @@ -9,10 +9,29 @@ #include #include #include -#include "./crypto/nostr_secp256k1.h" -// Include our ChaCha20 implementation -#include "crypto/nostr_chacha20.h" +// Forward declarations for crypto functions (private API) +// These functions are implemented in crypto/ but not exposed through public headers +int ecdh_shared_secret(const unsigned char* private_key, const unsigned char* public_key, unsigned char* shared_secret); +int nostr_secp256k1_get_random_bytes(unsigned char* buf, size_t len); + +// ChaCha20 functions for NIP-44 encryption +int chacha20_encrypt(const unsigned char key[32], unsigned int counter, + const unsigned char nonce[12], const unsigned char* input, + unsigned char* output, size_t length); + +// HKDF functions for NIP-44 key derivation +int nostr_hkdf_extract(const unsigned char* salt, size_t salt_len, + const unsigned char* ikm, size_t ikm_len, + unsigned char* prk); +int nostr_hkdf_expand(const unsigned char* prk, size_t prk_len, + const unsigned char* info, size_t info_len, + unsigned char* okm, size_t okm_len); + +// HMAC-SHA256 function for NIP-44 authentication +int nostr_hmac_sha256(const unsigned char* key, size_t key_len, + const unsigned char* data, size_t data_len, + unsigned char* hmac); // Forward declarations for internal functions static size_t calc_padded_len(size_t unpadded_len); diff --git a/nostr_core/nostr_core.h b/nostr_core/nostr_core.h index ff42acaa..e5f2219f 100644 --- a/nostr_core/nostr_core.h +++ b/nostr_core/nostr_core.h @@ -1,11 +1,103 @@ #ifndef NOSTR_CORE_H #define NOSTR_CORE_H -/** - * NOSTR Core Library - Unified Header File +/* + * NOSTR Core Library - Complete API Reference * - * This file provides a single include point for all NOSTR Core functionality. - * Include this file to access all available NIPs and core functions. + * This header includes ALL library functionality. For modular includes, + * use individual headers instead. + * + * ============================================================================ + * QUICK FUNCTION REFERENCE - Find what you need fast! + * ============================================================================ + * + * EVENT OPERATIONS (NIP-01): + * - nostr_create_and_sign_event() -> Create and sign new Nostr events + * - nostr_validate_event() -> Validate complete Nostr event + * - nostr_validate_event_structure() -> Check event structure only + * - nostr_verify_event_signature() -> Verify cryptographic signature + * + * CRYPTOGRAPHIC HASHING: + * - nostr_sha256() -> Single-call SHA-256 hash + * - nostr_sha256_init() -> Initialize streaming SHA-256 context + * - nostr_sha256_update() -> Process data chunks incrementally + * - nostr_sha256_final() -> Finalize hash and clear context + * - nostr_sha256_file_stream() -> Hash large files efficiently (NEW!) + * - nostr_sha512() -> SHA-512 hash function + * - nostr_hmac_sha256() -> HMAC with SHA-256 + * - nostr_hmac_sha512() -> HMAC with SHA-512 + * + * DIGITAL SIGNATURES & KEYS: + * - nostr_schnorr_sign() -> Create Schnorr signatures + * - nostr_ec_public_key_from_private_key() -> Generate public keys + * - nostr_ec_private_key_verify() -> Validate private key format + * - nostr_ec_sign() -> ECDSA signature creation + * - nostr_rfc6979_generate_k() -> Deterministic nonce generation + * + * NIP-04 ENCRYPTION (Legacy): + * - nostr_nip04_encrypt() -> Encrypt messages (AES-256-CBC) + * - nostr_nip04_decrypt() -> Decrypt messages (AES-256-CBC) + * + * NIP-44 ENCRYPTION (Modern - Recommended): + * - nostr_nip44_encrypt() -> Encrypt with ChaCha20 + HMAC + * - nostr_nip44_encrypt_with_nonce() -> Encrypt with specific nonce (testing) + * - nostr_nip44_decrypt() -> Decrypt ChaCha20 + HMAC messages + * + * BIP39 MNEMONICS: + * - nostr_bip39_mnemonic_from_bytes() -> Generate mnemonic from entropy + * - nostr_bip39_mnemonic_validate() -> Validate mnemonic phrase + * - nostr_bip39_mnemonic_to_seed() -> Convert mnemonic to seed + * + * BIP32 HD WALLETS: + * - nostr_bip32_key_from_seed() -> Create master key from seed + * - nostr_bip32_derive_child() -> Derive child key from parent + * - nostr_bip32_derive_path() -> Derive keys from derivation path + * + * KEY DERIVATION: + * - nostr_hkdf() -> HKDF key derivation (full) + * - nostr_hkdf_extract() -> HKDF extract step only + * - nostr_hkdf_expand() -> HKDF expand step only + * - nostr_pbkdf2_hmac_sha512() -> PBKDF2 with HMAC-SHA512 + * - ecdh_shared_secret() -> ECDH shared secret computation + * + * UTILITIES & ENCODING: + * - nostr_bytes_to_hex() -> Convert bytes to hex string + * - nostr_hex_to_bytes() -> Convert hex string to bytes + * - base64_encode() -> Base64 encoding + * - base64_decode() -> Base64 decoding + * + * SYSTEM FUNCTIONS: + * - nostr_crypto_init() -> Initialize crypto subsystem + * - nostr_crypto_cleanup() -> Cleanup crypto subsystem + * + * ============================================================================ + * USAGE EXAMPLES: + * ============================================================================ + * + * Basic Event Creation: + * cJSON* event = nostr_create_and_sign_event(1, "Hello Nostr!", NULL, private_key, time(NULL)); + * if (nostr_validate_event(event) == NOSTR_SUCCESS) { ... } + * + * Streaming SHA-256 (for large files): + * nostr_sha256_ctx_t ctx; + * nostr_sha256_init(&ctx); + * // Process data in chunks... + * nostr_sha256_update(&ctx, data, data_size); + * nostr_sha256_final(&ctx, hash_output); + * + * File Hashing: + * unsigned char file_hash[32]; + * nostr_sha256_file_stream("large_video.mp4", file_hash); + * + * Modern Encryption (NIP-44): + * nostr_nip44_encrypt(sender_key, recipient_pubkey, "secret message", output, sizeof(output)); + * + * HD Wallet Derivation: + * nostr_bip32_key_from_seed(seed, 64, &master_key); + * uint32_t path[] = {44, 1237, 0, 0, 0}; // m/44'/1237'/0'/0/0 + * nostr_bip32_derive_path(&master_key, path, 5, &derived_key); + * + * ============================================================================ */ #ifdef __cplusplus diff --git a/nostr_core/utils.c b/nostr_core/utils.c index 3069c003..06545930 100644 --- a/nostr_core/utils.c +++ b/nostr_core/utils.c @@ -9,14 +9,28 @@ #include #include -// Include our secp256k1 wrapper for elliptic curve operations -#include "crypto/nostr_secp256k1.h" +// Forward declarations for crypto functions (private API) +// These functions are implemented in crypto/ but not exposed through public headers -// Include our self-contained AES implementation for NIP-04 -#include "crypto/nostr_aes.h" +// secp256k1 functions +typedef struct { + unsigned char data[64]; +} nostr_secp256k1_pubkey; -// Include our ChaCha20 implementation for NIP-44 -#include "crypto/nostr_chacha20.h" +typedef struct { + unsigned char data[96]; +} nostr_secp256k1_keypair; + +int nostr_secp256k1_context_create(void); +void nostr_secp256k1_context_destroy(void); +int nostr_secp256k1_ec_pubkey_parse(nostr_secp256k1_pubkey* pubkey, const unsigned char* input, size_t inputlen); +int nostr_secp256k1_ecdh(unsigned char* output, const nostr_secp256k1_pubkey* pubkey, const unsigned char* privkey, void* hashfp, void* data); +int nostr_secp256k1_ec_seckey_verify(const unsigned char* seckey); +int nostr_secp256k1_ec_pubkey_create(nostr_secp256k1_pubkey* pubkey, const unsigned char* privkey); +int nostr_secp256k1_ec_pubkey_serialize_compressed(unsigned char* output, const nostr_secp256k1_pubkey* pubkey); +int nostr_secp256k1_keypair_create(nostr_secp256k1_keypair* keypair, const unsigned char* privkey); +int nostr_secp256k1_schnorrsig_sign32(unsigned char* sig, const unsigned char* msg32, const nostr_secp256k1_keypair* keypair, const unsigned char* aux_rand32); +int nostr_secp256k1_ec_seckey_tweak_add(unsigned char* seckey, const unsigned char* tweak); // ============================================================================= @@ -328,6 +342,125 @@ int nostr_sha256(const unsigned char* data, size_t len, unsigned char* hash) { return 0; } +// ============================================================================= +// STREAMING SHA-256 IMPLEMENTATION +// ============================================================================= + +int nostr_sha256_init(nostr_sha256_ctx_t* ctx) { + if (!ctx) return -1; + + // Initialize SHA-256 state + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; + + // Initialize counters and buffer + ctx->bitlen = 0; + ctx->buflen = 0; + + return 0; +} + +int nostr_sha256_update(nostr_sha256_ctx_t* ctx, const unsigned char* data, size_t len) { + if (!ctx || !data) return -1; + + for (size_t i = 0; i < len; i++) { + ctx->buffer[ctx->buflen] = data[i]; + ctx->buflen++; + + // Process complete blocks + if (ctx->buflen == 64) { + sha256_transform(ctx->state, ctx->buffer); + ctx->bitlen += 512; // 64 bytes * 8 bits + ctx->buflen = 0; + } + } + + return 0; +} + +int nostr_sha256_final(nostr_sha256_ctx_t* ctx, unsigned char* hash) { + if (!ctx || !hash) return -1; + + // Calculate final bit length + uint64_t final_bitlen = ctx->bitlen + (ctx->buflen * 8); + + // Pad the message + ctx->buffer[ctx->buflen] = 0x80; + ctx->buflen++; + + // If not enough space for length, pad and process another block + if (ctx->buflen > 56) { + while (ctx->buflen < 64) { + ctx->buffer[ctx->buflen] = 0x00; + ctx->buflen++; + } + sha256_transform(ctx->state, ctx->buffer); + ctx->buflen = 0; + } + + // Pad with zeros up to 56 bytes + while (ctx->buflen < 56) { + ctx->buffer[ctx->buflen] = 0x00; + ctx->buflen++; + } + + // Append length as big-endian 64-bit integer + for (int i = 0; i < 8; i++) { + ctx->buffer[56 + i] = (final_bitlen >> (56 - i * 8)) & 0xff; + } + + // Process final block + sha256_transform(ctx->state, ctx->buffer); + + // Convert state to output bytes + for (int i = 0; i < 8; i++) { + hash[i * 4] = (ctx->state[i] >> 24) & 0xff; + hash[i * 4 + 1] = (ctx->state[i] >> 16) & 0xff; + hash[i * 4 + 2] = (ctx->state[i] >> 8) & 0xff; + hash[i * 4 + 3] = ctx->state[i] & 0xff; + } + + // Clear sensitive data + memory_clear(ctx, sizeof(nostr_sha256_ctx_t)); + + return 0; +} + +int nostr_sha256_file_stream(const char* filename, unsigned char* hash) { + if (!filename || !hash) return -1; + + FILE* file = fopen(filename, "rb"); + if (!file) return -1; + + nostr_sha256_ctx_t ctx; + if (nostr_sha256_init(&ctx) != 0) { + fclose(file); + return -1; + } + + // Process file in 4KB chunks for memory efficiency + unsigned char buffer[4096]; + size_t bytes_read; + + while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) { + if (nostr_sha256_update(&ctx, buffer, bytes_read) != 0) { + fclose(file); + return -1; + } + } + + fclose(file); + + // Finalize and return result + return nostr_sha256_final(&ctx, hash); +} + // ============================================================================= // HMAC IMPLEMENTATION // ============================================================================= diff --git a/nostr_core/utils.h b/nostr_core/utils.h index dae184a3..a1b927b8 100644 --- a/nostr_core/utils.h +++ b/nostr_core/utils.h @@ -45,6 +45,30 @@ void nostr_crypto_cleanup(void); // SHA-256 hash function int nostr_sha256(const unsigned char *data, size_t len, unsigned char *hash); +// ============================================================================= +// STREAMING SHA-256 FUNCTIONS +// ============================================================================= + +// SHA-256 streaming context +typedef struct { + uint32_t state[8]; // Current hash state + unsigned char buffer[64]; // Input buffer for incomplete blocks + uint64_t bitlen; // Total bits processed + size_t buflen; // Current buffer length +} nostr_sha256_ctx_t; + +// Initialize SHA-256 streaming context +int nostr_sha256_init(nostr_sha256_ctx_t* ctx); + +// Update SHA-256 context with new data +int nostr_sha256_update(nostr_sha256_ctx_t* ctx, const unsigned char* data, size_t len); + +// Finalize SHA-256 and output hash +int nostr_sha256_final(nostr_sha256_ctx_t* ctx, unsigned char* hash); + +// Stream SHA-256 hash of a file +int nostr_sha256_file_stream(const char* filename, unsigned char* hash); + // HMAC-SHA256 int nostr_hmac_sha256(const unsigned char *key, size_t key_len, const unsigned char *data, size_t data_len, diff --git a/tests/chacha20_test.c b/tests/chacha20_test.c index 11e250de..4e57eafa 100644 --- a/tests/chacha20_test.c +++ b/tests/chacha20_test.c @@ -9,7 +9,15 @@ #include #include #include -#include "../nostr_core/crypto/nostr_chacha20.h" + +// Forward declarations for ChaCha20 functions (private API) +// These functions are implemented in crypto/ but not exposed through public headers +void chacha20_quarter_round(uint32_t state[16], int a, int b, int c, int d); +int chacha20_block(const uint8_t key[32], uint32_t counter, + const uint8_t nonce[12], uint8_t output[64]); +int chacha20_encrypt(const uint8_t key[32], uint32_t counter, + const uint8_t nonce[12], const uint8_t* input, + uint8_t* output, size_t length); // Helper function to convert hex string to bytes static int hex_to_bytes(const char* hex, uint8_t* bytes, size_t len) { diff --git a/tests/debug.log b/tests/debug.log index 1264c80d..b46110ea 100644 --- a/tests/debug.log +++ b/tests/debug.log @@ -6,3 +6,11 @@ }] [10:24:53.944] RECV nostr.mom:443: ["EVENT","sync_0_1755354293",{"content":"GM๐Ÿซก","created_at":1755354265,"id":"3e7c67349dd3e1ccaaf4dcd6f5987451d63561b14cdff6c6e68cc5448ec5acaf","kind":1,"pubkey":"ff2f4cd786e42b4323749c91517ec7baf22dfd035b7a101bea83b6e2bcbacd15","sig":"2278afa7b7f42b68f8b3f393bb72b6af4b2a34b77008e109232e24bcfe8a3d1ce917187ef1ca68f4a69e52c2067c14da03ed63e31e4137b1175f8ee1a08b7c21","tags":[["e","45983e18b7c0f28933ecd1c4ead88eb5561caa465a5bc939914b64fa455aefc3","wss://relay.primal.net","root"],["p","db625e7637543ca7d7be65025834db318a0c7b75b0e23d4fb9e39229f5ba6fa7","","mention"]]}] [10:24:53.944] SEND nostr.mom:443: ["CLOSE", "sync_0_1755354293"] + +=== NOSTR WebSocket Debug Log Started === +[12:34:34.841] SEND nostr.mom:443: ["REQ", "sync_0_1756830874", { + "kinds": [1], + "limit": 1 + }] +[12:34:34.997] RECV nostr.mom:443: ["EVENT","sync_0_1756830874",{"content":"It's a lot of work. ๐Ÿซ‚๐Ÿซ‚๐Ÿซ‚","created_at":1756830871,"id":"dd1db1b8e25c1278b6dc47b2a05633854a1afb936ea035a06182f4e0683a732e","kind":1,"pubkey":"3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24","sig":"33ef0e09477fc978fccfe57d40856952af01b7e413a6174cb99e1d39e0639ba75d5c300f74bd2945c48275debb53f80e7f6a5cc91a4511323b756f5e09707969","tags":[["alt","A short note: It's a lot of work. ๐Ÿซ‚๐Ÿซ‚๐Ÿซ‚"],["e","33597a2e46cffec3da6500c5ddc3cfcf8248e065377e9a5abea23cf633a7c134","wss://nos.lol/","root","91c9a5e1a9744114c6fe2d61ae4de82629eaaa0fb52f48288093c7e7e036f832"],["p","91c9a5e1a9744114c6fe2d61ae4de82629eaaa0fb52f48288093c7e7e036f832","wss://nos.lol/"]]}] +[12:34:34.998] SEND nostr.mom:443: ["CLOSE", "sync_0_1756830874"] diff --git a/tests/nip01_validation_test b/tests/nip01_test similarity index 91% rename from tests/nip01_validation_test rename to tests/nip01_test index e8234bf0..8fac1380 100755 Binary files a/tests/nip01_validation_test and b/tests/nip01_test differ diff --git a/tests/nip01_validation_test.c b/tests/nip01_test.c similarity index 100% rename from tests/nip01_validation_test.c rename to tests/nip01_test.c diff --git a/tests/nip04_comparison_test b/tests/nip04_comparison_test deleted file mode 100755 index c7d44f5f..00000000 Binary files a/tests/nip04_comparison_test and /dev/null differ diff --git a/tests/nip04_comparison_test.c b/tests/nip04_comparison_test.c deleted file mode 100644 index 30f20a24..00000000 --- a/tests/nip04_comparison_test.c +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include -#include -#include "../nostr_core/nip004.h" -#include "../nostr_core/nostr_common.h" -#include "../nostr_core/utils.h" - -int main(void) { - printf("=== NIP-04 DEBUG COMPARISON (C) ===\n"); - - // Initialize NOSTR library - REQUIRED for secp256k1 operations - if (nostr_init() != NOSTR_SUCCESS) { - printf("โŒ Failed to initialize NOSTR library\n"); - return 1; - } - printf("โœ“ NOSTR library initialized successfully\n"); - - // Test vectors matching JavaScript - const char* sk1_hex = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe"; - const char* pk1_hex = "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1"; - const char* sk2_hex = "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220"; - const char* pk2_hex = "dcb33a629560280a0ee3b6b99b68c044fe8914ad8a984001ebf6099a9b474dc3"; - const char* plaintext = "nanana"; - const char* expectedCiphertext = "d6Joav5EciPI9hdHw31vmQ==?iv=fWs5rfv2+532arG/k83kcA=="; - - // Convert hex keys to bytes - unsigned char sk1[32], pk1[32], sk2[32], pk2[32]; - nostr_hex_to_bytes(sk1_hex, sk1, 32); - nostr_hex_to_bytes(pk1_hex, pk1, 32); - nostr_hex_to_bytes(sk2_hex, sk2, 32); - nostr_hex_to_bytes(pk2_hex, pk2, 32); - - // Print keys for comparison - printf("[C] Private Key sk1: %s\n", sk1_hex); - printf("[C] Public Key pk2: %s\n", pk2_hex); - - // Allocate output buffer for encryption - char* encrypted = malloc(NOSTR_NIP04_MAX_ENCRYPTED_SIZE); - if (!encrypted) { - printf("Memory allocation failed\n"); - return 1; - } - - printf("\n--- ENCRYPTION TEST ---\n"); - printf("[C] Encrypting \"%s\" using sk1 -> pk2\n", plaintext); - - // Call the encryption function - int result = nostr_nip04_encrypt(sk1, pk2, plaintext, encrypted, NOSTR_NIP04_MAX_ENCRYPTED_SIZE); - - if (result == NOSTR_SUCCESS) { - printf("[C] Encrypted Result: %s\n", encrypted); - } else { - printf("Encryption Error: %s\n", nostr_strerror(result)); - free(encrypted); - return 1; - } - - printf("\n--- DECRYPTION TEST ---\n"); - printf("[C] Decrypting \"%s\" using sk2 + pk1\n", expectedCiphertext); - printf("[C] Private Key sk2: %s\n", sk2_hex); - printf("[C] Public Key pk1: %s\n", pk1_hex); - - // Allocate output buffer for decryption - char* decrypted = malloc(1000); - if (!decrypted) { - printf("Memory allocation failed\n"); - free(encrypted); - return 1; - } - - // Call the decryption function - result = nostr_nip04_decrypt(sk2, pk1, expectedCiphertext, decrypted, 1000); - - if (result == NOSTR_SUCCESS) { - printf("[C] UTF-8 Decoded: \"%s\"\n", decrypted); - - printf("\n--- RESULTS ---\n"); - printf("Encryption Success: Generated ciphertext\n"); - printf("Decryption Success: %s\n", strcmp(decrypted, plaintext) == 0 ? "true" : "false"); - printf("Expected: \"%s\"\n", plaintext); - printf("Got: \"%s\"\n", decrypted); - } else { - printf("Decryption Error: %s\n", nostr_strerror(result)); - } - - free(encrypted); - free(decrypted); - - // Cleanup NOSTR library - nostr_cleanup(); - return 0; -} diff --git a/tests/old/relay_pool_test.c b/tests/old/relay_pool_test.c deleted file mode 100644 index ffa988da..00000000 --- a/tests/old/relay_pool_test.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * NOSTR Relay Pool Test Program (READ-ONLY) - * - * Tests the relay pool event processing functionality by: - * - Creating a pool with hardcoded relays - * - Subscribing to kind 1 events (text notes) from other users - * - Using the new event processing functions - * - Displaying raw data output without interpretation - * - * IMPORTANT: This test is READ-ONLY and never publishes events. - * It only sends REQ (subscription) messages and receives EVENT responses. - * Any test events seen in output are from other users or previous test runs. - */ - -#include -#include -#include -#include -#include -#include -#include "../nostr_core/nostr_core.h" -#include "../cjson/cJSON.h" - -// Global variables for clean shutdown -static volatile int keep_running = 1; -static nostr_relay_pool_t* g_pool = NULL; -static nostr_pool_subscription_t* g_subscription = NULL; - -// Statistics tracking -static int events_received = 0; -static int events_per_relay[3] = {0, 0, 0}; // Track events per relay -static const char* relay_urls[] = { - "wss://relay.laantungir.net", - "ws://127.0.0.1:7777", - "wss://nostr.mom" -}; -static const int relay_count = 3; - -// Signal handler for clean shutdown -void signal_handler(int sig) { - (void)sig; // Unused parameter - printf("\n๐Ÿ›‘ Received shutdown signal, cleaning up...\n"); - keep_running = 0; -} - -// Event callback - called when events are received -void on_event_received(cJSON* event, const char* relay_url, void* user_data) { - (void)user_data; // Unused parameter - - events_received++; - - // Track events per relay - for (int i = 0; i < relay_count; i++) { - if (strcmp(relay_url, relay_urls[i]) == 0) { - events_per_relay[i]++; - break; - } - } - - // Print raw event data - char* event_json = cJSON_Print(event); - if (event_json) { - printf("\n๐Ÿ“จ EVENT from %s:\n", relay_url); - printf("Raw JSON: %s\n", event_json); - printf("---\n"); - free(event_json); - } - - // Also extract and display key fields for readability - cJSON* id = cJSON_GetObjectItem(event, "id"); - cJSON* pubkey = cJSON_GetObjectItem(event, "pubkey"); - cJSON* created_at = cJSON_GetObjectItem(event, "created_at"); - cJSON* content = cJSON_GetObjectItem(event, "content"); - - printf("๐Ÿ“„ Parsed fields:\n"); - if (id && cJSON_IsString(id)) { - printf(" ID: %s\n", cJSON_GetStringValue(id)); - } - if (pubkey && cJSON_IsString(pubkey)) { - printf(" Author: %s\n", cJSON_GetStringValue(pubkey)); - } - if (created_at && cJSON_IsNumber(created_at)) { - time_t timestamp = (time_t)cJSON_GetNumberValue(created_at); - printf(" Created: %s", ctime(×tamp)); - } - if (content && cJSON_IsString(content)) { - const char* text = cJSON_GetStringValue(content); - printf(" Content: %.100s%s\n", text, strlen(text) > 100 ? "..." : ""); - } - printf("===============================\n"); -} - -// EOSE callback - called when all relays have sent "End of Stored Events" -void on_eose_received(void* user_data) { - (void)user_data; // Unused parameter - printf("โœ… EOSE: All relays have finished sending stored events\n"); -} - -// Display relay status -void display_relay_status() { - char** urls; - nostr_pool_relay_status_t* statuses; - - int count = nostr_relay_pool_list_relays(g_pool, &urls, &statuses); - if (count > 0) { - printf("\n๐Ÿ”— RELAY STATUS:\n"); - for (int i = 0; i < count; i++) { - const char* status_icon; - const char* status_text; - - switch (statuses[i]) { - case NOSTR_POOL_RELAY_CONNECTED: - status_icon = "๐ŸŸข"; - status_text = "Connected"; - break; - case NOSTR_POOL_RELAY_CONNECTING: - status_icon = "๐ŸŸก"; - status_text = "Connecting..."; - break; - case NOSTR_POOL_RELAY_DISCONNECTED: - status_icon = "๐Ÿ”ด"; - status_text = "Disconnected"; - break; - case NOSTR_POOL_RELAY_ERROR: - status_icon = "โŒ"; - status_text = "Error"; - break; - default: - status_icon = "โ“"; - status_text = "Unknown"; - break; - } - - // Get publish and query latency statistics - double query_latency = nostr_relay_pool_get_relay_query_latency(g_pool, urls[i]); - const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(g_pool, urls[i]); - - // Get events count from relay statistics (more accurate) - int relay_events = 0; - if (stats) { - relay_events = stats->events_received; - } else { - // Fallback to local counter - for (int j = 0; j < relay_count; j++) { - if (strcmp(urls[i], relay_urls[j]) == 0) { - relay_events = events_per_relay[j]; - break; - } - } - } - - // Display status with latency information - if (query_latency >= 0.0) { - printf(" %s %-25s %s (query: %.0fms, events: %d)\n", - status_icon, urls[i], status_text, query_latency, relay_events); - } else { - printf(" %s %-25s %s (query: ---, events: %d)\n", - status_icon, urls[i], status_text, relay_events); - } - - // Show additional latency statistics if available - if (stats) { - if (stats->publish_samples > 0) { - printf(" ๐Ÿ“Š Publish latency: avg=%.0fms (%d samples)\n", - stats->publish_latency_avg, stats->publish_samples); - } - if (stats->query_samples > 0) { - printf(" ๐Ÿ“Š Query latency: avg=%.0fms (%d samples)\n", - stats->query_latency_avg, stats->query_samples); - } - if (stats->events_published > 0) { - printf(" ๐Ÿ“ค Published: %d events (%d OK, %d failed)\n", - stats->events_published, stats->events_published_ok, - stats->events_published_failed); - } - } - - free(urls[i]); - } - free(urls); - free(statuses); - - printf("๐Ÿ“Š Total events received: %d\n", events_received); - } -} - -int main() { - printf("๐Ÿš€ NOSTR Relay Pool Test Program\n"); - printf("=================================\n"); - printf("Testing relays:\n"); - for (int i = 0; i < relay_count; i++) { - printf(" - %s\n", relay_urls[i]); - } - printf("\n"); - - // Set up signal handler for clean shutdown - signal(SIGINT, signal_handler); - signal(SIGTERM, signal_handler); - - // Initialize NOSTR core library - printf("๐Ÿ”ง Initializing NOSTR core library...\n"); - if (nostr_init() != NOSTR_SUCCESS) { - fprintf(stderr, "โŒ Failed to initialize NOSTR core library\n"); - return 1; - } - - // Create relay pool - printf("๐ŸŠ Creating relay pool...\n"); - g_pool = nostr_relay_pool_create(); - if (!g_pool) { - fprintf(stderr, "โŒ Failed to create relay pool\n"); - nostr_cleanup(); - return 1; - } - - // Add relays to pool - printf("โž• Adding relays to pool...\n"); - for (int i = 0; i < relay_count; i++) { - printf(" Adding: %s\n", relay_urls[i]); - int result = nostr_relay_pool_add_relay(g_pool, relay_urls[i]); - if (result != NOSTR_SUCCESS) { - printf(" โš ๏ธ Warning: Failed to add relay %s (error: %s)\n", - relay_urls[i], nostr_strerror(result)); - } - } - - // Create filter for kind 1 events (text notes) - printf("๐Ÿ” Creating subscription filter for kind 1 events...\n"); - cJSON* filter = cJSON_CreateObject(); - if (!filter) { - fprintf(stderr, "โŒ Failed to create filter\n"); - nostr_relay_pool_destroy(g_pool); - nostr_cleanup(); - return 1; - } - - // Add kinds array with kind 1 (text notes) - cJSON* kinds = cJSON_CreateArray(); - cJSON_AddItemToArray(kinds, cJSON_CreateNumber(1)); - cJSON_AddItemToObject(filter, "kinds", kinds); - - // Limit to recent events to avoid flooding - cJSON_AddNumberToObject(filter, "limit", 1); - - // Subscribe to events from all relays - printf("๐Ÿ“ก Subscribing to events from all relays...\n"); - g_subscription = nostr_relay_pool_subscribe( - g_pool, - relay_urls, - relay_count, - filter, - on_event_received, - on_eose_received, - NULL - ); - - if (!g_subscription) { - fprintf(stderr, "โŒ Failed to create subscription\n"); - cJSON_Delete(filter); - nostr_relay_pool_destroy(g_pool); - nostr_cleanup(); - return 1; - } - - printf("โœ… Subscription created successfully!\n"); - printf("โฑ๏ธ Starting event processing...\n"); - printf(" (Press Ctrl+C to stop)\n\n"); - - // Display initial status - display_relay_status(); - - printf("๏ฟฝ Starting continuous monitoring...\n\n"); - - // Run event processing loop - time_t last_status_update = time(NULL); - - while (keep_running) { - // Process events for 1 second - int events_processed = nostr_relay_pool_run(g_pool, 1000); - - // Display status every 5 seconds - if (time(NULL) - last_status_update >= 5) { - display_relay_status(); - last_status_update = time(NULL); - } - - // Small status indicator - if (events_processed > 0) { - printf("."); - fflush(stdout); - } - } - - printf("\n\n๐Ÿ Test completed!\n"); - - // Final status display - display_relay_status(); - - // Cleanup - printf("๐Ÿงน Cleaning up...\n"); - if (g_subscription) { - nostr_pool_subscription_close(g_subscription); - } - if (g_pool) { - nostr_relay_pool_destroy(g_pool); - } - cJSON_Delete(filter); - nostr_cleanup(); - - printf("โœ… Test program finished successfully!\n"); - printf("๐Ÿ“ˆ Final stats:\n"); - printf(" Total events: %d\n", events_received); - for (int i = 0; i < relay_count; i++) { - printf(" %s: %d events\n", relay_urls[i], events_per_relay[i]); - } - - return 0; -} diff --git a/tests/old/test_vectors_display.c b/tests/old/test_vectors_display.c deleted file mode 100644 index c9b22352..00000000 --- a/tests/old/test_vectors_display.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * NIP-04 Test Vectors Display - All 6 Test Vectors - * Shows complete test vector integration even if runtime testing has issues - */ - -#include -#include -#include - -void display_test_vector(int num, const char* description, const char* sk1, const char* pk1, - const char* sk2, const char* pk2, const char* plaintext, const char* expected) { - printf("=== TEST VECTOR %d: %s ===\n", num, description); - printf("SK1 (Alice): %s\n", sk1); - printf("PK1 (Alice): %s\n", pk1); - printf("SK2 (Bob): %s\n", sk2); - printf("PK2 (Bob): %s\n", pk2); - printf("Plaintext: \"%s\"\n", plaintext); - - if (strlen(expected) > 80) { - char truncated[81]; - strncpy(truncated, expected, 80); - truncated[80] = '\0'; - printf("Expected: %s...\n", truncated); - } else { - printf("Expected: %s\n", expected); - } - printf("\n"); -} - -int main(void) { - printf("=== NIP-04 Test Vector Collection ===\n"); - printf("Complete integration of 6 test vectors (3 original + 3 from nostr-tools)\n\n"); - - // Original Test Vectors (1-3) - display_test_vector(1, "Basic NIP-04 Encryption", - "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe", - "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1", - "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220", - "dcb33a629560280a0ee3b6b99b68c044fe8914ad8a984001ebf6099a9b474dc3", - "nanana", - "zJxfaJ32rN5Dg1ODjOlEew==?iv=EV5bUjcc4OX2Km/zPp4ndQ=="); - - display_test_vector(2, "Large Payload Test (800 characters)", - "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe", - "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1", - "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220", - "dcb33a629560280a0ee3b6b99b68c044fe8914ad8a984001ebf6099a9b474dc3", - "800 'z' characters", - "6f8dMstm+udOu7yipSn33orTmwQpWbtfuY95NH+eTU1kArysWJIDkYgI2D25EAGIDJsNd45jOJ2NbVOhFiL3ZP/NWsTwXokk34iyHyA/lkjzugQ1bHXoMD1fP/Ay4hB4al1NHb8HXHKZaxPrErwdRDb8qa/I6dXb/1xxyVvNQBHHvmsM5yIFaPwnCN1DZqXf2KbTA/Ekz7Hy+7R+Sy3TXLQDFpWYqykppkXc7Fs0qSuPRyxz5+anuN0dxZa9GTwTEnBrZPbthKkNRrvZMdTGJ6WumOh9aUq8OJJWy9aOgsXvs7qjN1UqcCqQqYaVnEOhCaqWNDsVtsFrVDj+SaLIBvCiomwF4C4nIgngJ5I69tx0UNI0q+ZnvOGQZ7m1PpW2NYP7Yw43HJNdeUEQAmdCPnh/PJwzLTnIxHmQU7n7SPlMdV0SFa6H8y2HHvex697GAkyE5t8c2uO24OnqIwF1tR3blIqXzTSRl0GA6QvrSj2p4UtnWjvF7xT7RiIEyTtgU/AsihTrXyXzWWZaIBJogpgw6erlZqWjCH7sZy/WoGYEiblobOAqMYxax6vRbeuGtoYksr/myX+x9rfLrYuoDRTw4woXOLmMrrj+Mf0TbAgc3SjdkqdsPU1553rlSqIEZXuFgoWmxvVQDtekgTYyS97G81TDSK9nTJT5ilku8NVq2LgtBXGwsNIw/xekcOUzJke3kpnFPutNaexR1VF3ohIuqRKYRGcd8ADJP2lfwMcaGRiplAmFoaVS1YUhQwYFNq9rMLf7YauRGV4BJg/t9srdGxf5RoKCvRo+XM/nLxxysTR9MVaEP/3lDqjwChMxs+eWfLHE5vRWV8hUEqdrWNZV29gsx5nQpzJ4PARGZVu310pQzc6JAlc2XAhhFk6RamkYJnmCSMnb/RblzIATBi2kNrCVAlaXIon188inB62rEpZGPkRIP7PUfu27S/elLQHBHeGDsxOXsBRo1gl3te+raoBHsxo6zvRnYbwdAQa5taDE63eh+fT6kFI+xYmXNAQkU8Dp0MVhEh4JQI06Ni/AKrvYpC95TXXIphZcF+/Pv/vaGkhG2X9S3uhugwWK?iv=2vWkOQQi0WynNJz/aZ4k2g=="); - - display_test_vector(3, "Bidirectional Communication", - "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe", - "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1", - "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220", - "dcb33a629560280a0ee3b6b99b68c044fe8914ad8a984001ebf6099a9b474dc3", - "Hello Bob, this is Alice! / Hi Alice, Bob here. Message received!", - "Various encrypted messages"); - - printf("--- Generated with nostr-tools (Random Keys) ---\n\n"); - - // New Test Vectors Generated with nostr-tools (4-6) - display_test_vector(4, "Random Keys - Hello, NOSTR!", - "5c5ea5ec3a804533ba8a21ba3dd981fc55a84e854dde53869b3f812ccd788200", - "0988b20763d3f8bc06e88722f2aa6b3caed3cc510e93287e1ee3f70ed22f54d2", - "8e94e91ea679509ec1f5da2be87352ea78acde2b69563c23a41b7f07c0891bc3", - "13747a8025c1196da3e67ecf941aa889c5c4ec6773e7f325f3f8d2435c4603c6", - "Hello, NOSTR!", - "+bqZAkfv/tI4h0XcvB9Baw==?iv=Om7m3at5zjJjxyAQbFY2IQ=="); - - display_test_vector(5, "Long Message with Emoji", - "51099e755aaab7e8ee1850b683b673c11d09799e85a630e951eb3c92fab4aed3", - "c5fb1cad7b11e3cf7f31d5bf47aaf3398a4803ea786eedfd674f55fa55dcb649", - "41f2788d00bd362ac3c7c784ee46e35b99765a086514ee69cb15de38c072309a", - "ba6773cf6a9b11476f692d4681a2f1e3015d1ee4a8d7c9d0364bed120f225079", - "This is a longer message to test encryption with more content. ๐Ÿš€", - "3H9WEg9WjjN3r6ZymJt1R4ly3GlzhRR93FaSTGHLeM4oSS3eOnJtdXcO4ftgICMHRYM14WAmDDE9c12V8jhzua8GpnXKIVsNbY+oPF2yRwI=?iv=ztEGlo35pqJKrwZ2ZipsWg=="); - - display_test_vector(6, "Short Message", - "42c450eaebaee5ad94b602fc9054cde48f66d68c236b547aafee0ff319377290", - "a03f543eeb6c3f1c626181730751c39fd4f9f10455756d99ea855da97cf5076b", - "72f424c96239d271549c648d16635b5603ef32cdcbbff41058d14187b98f30cc", - "1c74b7a1d09ebeaf994a93a859682019930ad4f0f8ac7e65caacbbf4985042e8", - "Short", - "UIN92yHtAfX0vOTmn8VTtg==?iv=ou0QFU5UJUI6W4fUlkiElg=="); - - printf("=== SUMMARY ===\n"); - printf("โœ… Successfully generated 3 additional test vectors using nostr-tools\n"); - printf("โœ… All test vectors use genuine random nsec keys from the JavaScript ecosystem\n"); - printf("โœ… Test coverage includes: short, medium, long, Unicode, and emoji messages\n"); - printf("โœ… Enhanced from 3 to 6 comprehensive test vectors\n"); - printf("โœ… Ready for integration testing once library stability issues are resolved\n"); - printf("\n"); - printf("Files created:\n"); - printf("- test_vector_generator/generate_vectors.js (Vector generation script)\n"); - printf("- tests/nip04_test.c (Enhanced with 6 test vectors)\n"); - printf("- package.json (Node.js dependencies)\n"); - printf("\n"); - printf("๐ŸŽฏ Mission accomplished - Enhanced NIP-04 test coverage with nostr-tools vectors!\n"); - - return 0; -}