remove exposed .h crypto headers
This commit is contained in:
parent
c0d095e57b
commit
33129d82fd
|
@ -7,7 +7,7 @@ nips/
|
|||
node_modules/
|
||||
nostr-tools/
|
||||
tiny-AES-c/
|
||||
|
||||
blossom/
|
||||
|
||||
|
||||
Trash/debug_tests/
|
||||
|
|
148
README.md
148
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)
|
||||
[](VERSION)
|
||||
[](#license)
|
||||
[](#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*
|
||||
|
|
|
@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h> // 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
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
#ifndef _NOSTR_AES_H_
|
||||
#define _NOSTR_AES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.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
|
||||
};
|
||||
|
||||
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_
|
|
@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* ============================================================================
|
||||
* 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
|
||||
|
|
|
@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#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 */
|
|
@ -1,4 +1,3 @@
|
|||
#include "nostr_secp256k1.h"
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_schnorrsig.h>
|
||||
#include <secp256k1_ecdh.h>
|
||||
|
@ -6,6 +5,33 @@
|
|||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
#ifndef NOSTR_SECP256K1_H
|
||||
#define NOSTR_SECP256K1_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/** 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 */
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include "nip001.h"
|
||||
#include "utils.h"
|
||||
#include "crypto/nostr_secp256k1.h"
|
||||
#include "../cjson/cJSON.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -14,9 +13,21 @@
|
|||
#include <time.h>
|
||||
#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
|
||||
|
|
|
@ -6,13 +6,24 @@
|
|||
#include "nip004.h"
|
||||
#include "utils.h"
|
||||
#include "nostr_common.h"
|
||||
#include "crypto/nostr_secp256k1.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 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,
|
||||
|
|
|
@ -9,10 +9,29 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -9,14 +9,28 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 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
|
||||
// =============================================================================
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -9,7 +9,15 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#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) {
|
||||
|
|
|
@ -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"]
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,92 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#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("<EFBFBD> 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;
|
||||
}
|
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue