This commit is contained in:
Laan Tungir 2025-08-17 14:14:52 -04:00
parent 3ebfdc06c0
commit 1da4f6751e
25 changed files with 0 additions and 4851 deletions

View File

@ -1,309 +0,0 @@
# Exportable C NOSTR Library Design Guide
This document outlines the design principles and structure for making the C NOSTR library easily exportable and reusable across multiple projects.
## Overview
The C NOSTR library is designed as a modular, self-contained implementation that can be easily integrated into other projects while maintaining compatibility with the broader NOSTR ecosystem.
## Design Principles
### 1. Modular Architecture
- **Core Layer**: Essential NOSTR functionality (`nostr_core.c/h`)
- **Crypto Layer**: Self-contained cryptographic primitives (`nostr_crypto.c/h`)
- **WebSocket Layer**: Optional networking functionality (`nostr_websocket/`)
- **Dependencies**: Minimal external dependencies (only cJSON and mbedTLS)
### 2. Clean API Surface
- Consistent function naming with `nostr_` prefix
- Clear return codes using `NOSTR_SUCCESS`/`NOSTR_ERROR_*` constants
- Well-documented function signatures
- No global state where possible
### 3. Self-Contained Crypto
- Custom implementations of SHA-256, HMAC, secp256k1
- BIP39 wordlist embedded in code
- No external crypto library dependencies for core functionality
- Optional mbedTLS integration for enhanced security
### 4. Cross-Platform Compatibility
- Standard C99 code
- Platform-specific code isolated in separate modules
- ARM64 and x86_64 tested builds
- Static library compilation support
## Library Structure
```
c_nostr/
├── nostr_core.h # High-level API
├── nostr_core.c # Implementation
├── nostr_crypto.h # Crypto primitives API
├── nostr_crypto.c # Self-contained crypto
├── libnostr_core.a # Static library (x86_64)
├── libnostr_core.so # Shared library (x86_64)
├── cjson/ # JSON parsing (vendored)
├── mbedtls/ # Optional crypto backend
├── examples/ # Usage examples
├── tests/ # Test suites
└── nostr_websocket/ # Optional WebSocket layer
```
## Integration Methods
### Method 1: Static Library Linking
**Best for**: Applications that want a single binary with all NOSTR functionality embedded.
```bash
# Build the library
make lib
# In your project Makefile:
CFLAGS += -I/path/to/c_nostr
LDFLAGS += -L/path/to/c_nostr -lnostr_core -lm -static
```
**Usage:**
```c
#include "nostr_core.h"
int main() {
if (nostr_init() != NOSTR_SUCCESS) {
return 1;
}
unsigned char private_key[32], public_key[32];
nostr_generate_keypair(private_key, public_key);
cJSON* event = nostr_create_text_event("Hello NOSTR!", private_key);
// ... use event
nostr_cleanup();
return 0;
}
```
### Method 2: Source Code Integration
**Best for**: Applications that want to customize the crypto backend or minimize dependencies.
```bash
# Copy essential files to your project:
cp nostr_core.{c,h} your_project/src/
cp nostr_crypto.{c,h} your_project/src/
cp -r cjson/ your_project/src/
```
**Compile with your project:**
```c
// In your source
#include "nostr_core.h"
// Use NOSTR functions directly
```
### Method 3: Git Submodule
**Best for**: Projects that want to track upstream changes and contribute back.
```bash
# In your project root:
git submodule add https://github.com/yourorg/c_nostr.git deps/c_nostr
git submodule update --init --recursive
# In your Makefile:
CFLAGS += -Ideps/c_nostr
LDFLAGS += -Ldeps/c_nostr -lnostr_core
```
### Method 4: Shared Library
**Best for**: System-wide installation or multiple applications sharing the same NOSTR implementation.
```bash
# Install system-wide
sudo make install
# In your project:
CFLAGS += $(pkg-config --cflags nostr_core)
LDFLAGS += $(pkg-config --libs nostr_core)
```
## API Design for Exportability
### Consistent Error Handling
```c
typedef enum {
NOSTR_SUCCESS = 0,
NOSTR_ERROR_INVALID_INPUT = -1,
NOSTR_ERROR_CRYPTO_FAILED = -2,
NOSTR_ERROR_MEMORY_ALLOCATION = -3,
NOSTR_ERROR_JSON_PARSE = -4,
NOSTR_ERROR_NETWORK = -5
} nostr_error_t;
const char* nostr_strerror(int error_code);
```
### Clean Resource Management
```c
// Always provide cleanup functions
int nostr_init(void);
void nostr_cleanup(void);
// JSON objects returned should be freed by caller
cJSON* nostr_create_text_event(const char* content, const unsigned char* private_key);
// Caller must call cJSON_Delete(event) when done
```
### Optional Features
```c
// Compile-time feature toggles
#ifndef NOSTR_DISABLE_WEBSOCKETS
int nostr_connect_relay(const char* url);
#endif
#ifndef NOSTR_DISABLE_IDENTITY_PERSISTENCE
int nostr_save_identity(const unsigned char* private_key, const char* password, int account);
#endif
```
## Configuration System
### Build-Time Configuration
```c
// nostr_config.h (generated during build)
#define NOSTR_VERSION "1.0.0"
#define NOSTR_HAS_MBEDTLS 1
#define NOSTR_HAS_WEBSOCKETS 1
#define NOSTR_STATIC_BUILD 1
```
### Runtime Configuration
```c
typedef struct {
int log_level;
char* identity_file_path;
int default_account;
int enable_networking;
} nostr_config_t;
int nostr_set_config(const nostr_config_t* config);
const nostr_config_t* nostr_get_config(void);
```
## Testing and Validation
### Ecosystem Compatibility Testing
The library includes comprehensive test suites that validate compatibility with reference implementations like `nak`:
```bash
# Run all tests
cd tests && make test
# Test specific functionality
make test-crypto # Cryptographic primitives
make test-core # High-level NOSTR functions
```
### Test Vectors
Real-world test vectors are generated using `nak` to ensure ecosystem compatibility:
- Key generation and derivation (BIP39/BIP32)
- Event creation and signing
- Bech32 encoding/decoding
- Message serialization
## Documentation for Exporters
### Essential Files Checklist
For projects integrating this library, you need:
**Core Files (Required):**
- `nostr_core.h` - Main API
- `nostr_core.c` - Implementation
- `nostr_crypto.h` - Crypto API
- `nostr_crypto.c` - Self-contained crypto
- `cjson/` directory - JSON parsing
**Optional Files:**
- `nostr_websocket/` - WebSocket relay support
- `mbedtls/` - Enhanced crypto backend
- `examples/` - Usage examples
- `tests/` - Validation tests
### Minimal Integration Example
```c
// minimal_nostr.c - Smallest possible integration
#include "nostr_core.h"
int main() {
// Initialize library
nostr_init();
// Generate keypair
unsigned char priv[32], pub[32];
nostr_generate_keypair(priv, pub);
// Create and sign event
cJSON* event = nostr_create_text_event("Hello from my app!", priv);
char* json_string = cJSON_Print(event);
printf("Event: %s\n", json_string);
// Cleanup
free(json_string);
cJSON_Delete(event);
nostr_cleanup();
return 0;
}
```
**Compile:**
```bash
gcc -I. minimal_nostr.c nostr_core.c nostr_crypto.c cjson/cJSON.c -lm -o minimal_nostr
```
## Future Considerations
### Language Bindings
The C library is designed to be easily wrapped:
- **Python**: Use ctypes or cffi
- **JavaScript**: Use Node.js FFI or WASM compilation
- **Go**: Use cgo
- **Rust**: Use bindgen for FFI bindings
### WebAssembly Support
The library can be compiled to WebAssembly for browser usage:
```bash
emcc -O3 -s WASM=1 -s EXPORTED_FUNCTIONS='["_nostr_init", "_nostr_generate_keypair"]' \
nostr_core.c nostr_crypto.c cjson/cJSON.c -o nostr.wasm
```
### Package Manager Support
Future versions may include:
- pkgconfig files for system installation
- CMake integration for easier builds
- vcpkg/Conan package definitions
## Contributing Back
Projects using this library are encouraged to:
1. Report compatibility issues
2. Submit test vectors from their use cases
3. Contribute performance improvements
4. Add support for additional NIPs
## Version Compatibility
The library follows semantic versioning:
- **Major**: Breaking API changes
- **Minor**: New features, backward compatible
- **Patch**: Bug fixes
API stability guarantees:
- All functions prefixed with `nostr_` are part of the stable API
- Internal functions (static or prefixed with `_`) may change
- Configuration structures may be extended but not modified
---
This design ensures the C NOSTR library can be easily adopted by other projects while maintaining high compatibility with the NOSTR ecosystem standards.

View File

@ -1,29 +0,0 @@
# c-nostr Export and Implementation Guide
## Overview
This guide provides essential information for developers using the `c-nostr` library, particularly regarding the capabilities and limitations of its self-contained modules, such as the HTTP client.
## HTTP Client (`http_client.c`)
The `http_client` module is a lightweight, self-contained HTTP/HTTPS client designed for simple and direct web requests. It is ideal for tasks like fetching NIP-11 information from Nostr relays or interacting with standard, permissive web APIs.
### Key Features
* **Self-Contained**: It is built with `mbedTLS` and has no external dependencies beyond standard C libraries, making it highly portable.
* **Simplicity**: It provides a straightforward `http_get()` function for making web requests.
* **TLS Support**: It supports HTTPS and basic TLS 1.3/1.2 features, including SNI (Server Name Indication) and ALPN (Application-Layer Protocol Negotiation).
### Limitations
The `http_client` is intentionally simple and is **not a full-featured web browser**. Due to its minimalist design, it will likely be blocked by websites that employ advanced anti-bot protection systems.
* **Incompatibility with Advanced Bot Protection**: Sites like Google, Cloudflare, and others use sophisticated fingerprinting techniques to distinguish between real browsers and automated clients. They analyze the exact combination of TLS cipher suites, TLS extensions, and HTTP headers. Our client's fingerprint does not match a standard browser, so these sites will preemptively drop the connection, typically resulting in a `HTTP_ERROR_NETWORK` error.
* **Intended Use Case**: This client is best suited for interacting with known, friendly APIs and Nostr relays. It is **not** designed for general web scraping or for accessing services that are heavily guarded against automated traffic.
### Best Practices
* **Use for APIs and Relays**: Rely on this client for fetching data from well-defined, public endpoints that do not have aggressive bot-detection measures in place.
* **Avoid Protected Sites**: Do not attempt to use this client to access services like Google Search, as such attempts will fail. For those use cases, a full-featured library like `libcurl` or a dedicated web-scraping framework is required.
* **Check the Test Suite**: The `tests/http_client_test.c` file contains test cases that demonstrate both the successful use cases (e.g., `httpbin.org`) and the expected failures (e.g., `google.com`), providing a clear reference for the client's capabilities.

View File

@ -1,361 +0,0 @@
# Generic Automatic Version Increment System for Any Repository
Here's a generalized implementation guide for adding automatic versioning to any project:
## Core Concept
**Automatic patch version increment with each build** - Every build automatically increments the patch version: v0.1.0 → v0.1.1 → v0.1.2, etc.
## Implementation Steps
### 1. Add Version Increment Function to Build Script
Add this function to your build script (bash example):
```bash
# Function to automatically increment version
increment_version() {
echo "[INFO] Incrementing version..."
# Check if we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo "[WARNING] Not in a git repository - skipping version increment"
return 0
fi
# Get the highest version tag (not chronologically latest)
LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "v0.1.0")
if [[ -z "$LATEST_TAG" ]]; then
LATEST_TAG="v0.1.0"
fi
# Extract version components (remove 'v' prefix)
VERSION=${LATEST_TAG#v}
# Parse major.minor.patch using regex
if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
MAJOR=${BASH_REMATCH[1]}
MINOR=${BASH_REMATCH[2]}
PATCH=${BASH_REMATCH[3]}
else
echo "[ERROR] Invalid version format in tag: $LATEST_TAG"
echo "[ERROR] Expected format: v0.1.0"
return 1
fi
# Increment patch version
NEW_PATCH=$((PATCH + 1))
NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}"
echo "[INFO] Current version: $LATEST_TAG"
echo "[INFO] New version: $NEW_VERSION"
# Create new git tag
if git tag "$NEW_VERSION" 2>/dev/null; then
echo "[SUCCESS] Created new version tag: $NEW_VERSION"
else
echo "[WARNING] Tag $NEW_VERSION already exists - using existing version"
NEW_VERSION=$LATEST_TAG
fi
# Update VERSION file for compatibility
echo "${NEW_VERSION#v}" > VERSION
echo "[SUCCESS] Updated VERSION file to ${NEW_VERSION#v}"
}
```
### 2. Generate Version Header Files (For C/C++ Projects)
Add this to the increment_version function:
```bash
# Generate version.h header file (adjust path as needed)
cat > src/version.h << EOF
/*
* Auto-Generated Version Header
* DO NOT EDIT THIS FILE MANUALLY - Generated by build script
*/
#ifndef VERSION_H
#define VERSION_H
#define VERSION_MAJOR ${MAJOR}
#define VERSION_MINOR ${MINOR}
#define VERSION_PATCH ${NEW_PATCH}
#define VERSION_STRING "${MAJOR}.${MINOR}.${NEW_PATCH}"
#define VERSION_TAG "${NEW_VERSION}"
/* Build information */
#define BUILD_DATE "$(date +%Y-%m-%d)"
#define BUILD_TIME "$(date +%H:%M:%S)"
#define BUILD_TIMESTAMP "$(date '+%Y-%m-%d %H:%M:%S')"
/* Git information */
#define GIT_HASH "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
#define GIT_BRANCH "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')"
/* Display versions */
#define VERSION_DISPLAY "${NEW_VERSION}"
#define VERSION_FULL_DISPLAY "${NEW_VERSION} ($(date '+%Y-%m-%d %H:%M:%S'), $(git rev-parse --short HEAD 2>/dev/null || echo 'unknown'))"
/* Version API functions */
const char* get_version(void);
const char* get_version_full(void);
const char* get_build_info(void);
#endif /* VERSION_H */
EOF
# Generate version.c implementation file
cat > src/version.c << EOF
/*
* Auto-Generated Version Implementation
* DO NOT EDIT THIS FILE MANUALLY - Generated by build script
*/
#include "version.h"
const char* get_version(void) {
return VERSION_TAG;
}
const char* get_version_full(void) {
return VERSION_FULL_DISPLAY;
}
const char* get_build_info(void) {
return "Built on " BUILD_DATE " at " BUILD_TIME " from commit " GIT_HASH " on branch " GIT_BRANCH;
}
EOF
```
### 3. Generate Version File for Other Languages
**Python (`src/__version__.py`):**
```bash
cat > src/__version__.py << EOF
"""Auto-generated version file"""
__version__ = "${MAJOR}.${MINOR}.${NEW_PATCH}"
__version_tag__ = "${NEW_VERSION}"
__build_date__ = "$(date +%Y-%m-%d)"
__build_time__ = "$(date +%H:%M:%S)"
__git_hash__ = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
__git_branch__ = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')"
EOF
```
**JavaScript/Node.js (update `package.json`):**
```bash
# Update package.json version field
if [ -f package.json ]; then
sed -i "s/\"version\": \".*\"/\"version\": \"${MAJOR}.${MINOR}.${NEW_PATCH}\"/" package.json
fi
```
**Rust (update `Cargo.toml`):**
```bash
if [ -f Cargo.toml ]; then
sed -i "s/^version = \".*\"/version = \"${MAJOR}.${MINOR}.${NEW_PATCH}\"/" Cargo.toml
fi
```
**Go (generate `version.go`):**
```bash
cat > version.go << EOF
// Auto-generated version file
package main
const (
VersionMajor = ${MAJOR}
VersionMinor = ${MINOR}
VersionPatch = ${NEW_PATCH}
VersionString = "${MAJOR}.${MINOR}.${NEW_PATCH}"
VersionTag = "${NEW_VERSION}"
BuildDate = "$(date +%Y-%m-%d)"
BuildTime = "$(date +%H:%M:%S)"
GitHash = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
GitBranch = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')"
)
EOF
```
**Java (generate `Version.java`):**
```bash
cat > src/main/java/Version.java << EOF
// Auto-generated version class
public class Version {
public static final int VERSION_MAJOR = ${MAJOR};
public static final int VERSION_MINOR = ${MINOR};
public static final int VERSION_PATCH = ${NEW_PATCH};
public static final String VERSION_STRING = "${MAJOR}.${MINOR}.${NEW_PATCH}";
public static final String VERSION_TAG = "${NEW_VERSION}";
public static final String BUILD_DATE = "$(date +%Y-%m-%d)";
public static final String BUILD_TIME = "$(date +%H:%M:%S)";
public static final String GIT_HASH = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')";
public static final String GIT_BRANCH = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')";
}
EOF
```
### 4. Integrate into Build Targets
Call `increment_version` before your main build commands:
```bash
build_library() {
increment_version
echo "[INFO] Building library..."
# Your actual build commands here
make clean && make
}
build_release() {
increment_version
echo "[INFO] Building release..."
# Your release build commands
}
build_package() {
increment_version
echo "[INFO] Building package..."
# Your packaging commands
}
```
### 5. Update .gitignore
Add generated version files to `.gitignore`:
```gitignore
# Auto-generated version files
src/version.h
src/version.c
src/__version__.py
version.go
src/main/java/Version.java
VERSION
```
### 6. Update Build System Files
**For Makefile projects:**
```makefile
# Add version.c to your source files
SOURCES = main.c utils.c version.c
```
**For CMake projects:**
```cmake
# Add version files to your target
target_sources(your_target PRIVATE src/version.c)
```
**For Node.js projects:**
```json
{
"scripts": {
"build": "node build.js && increment_version",
"version": "node -e \"console.log(require('./package.json').version)\""
}
}
```
### 7. Create Initial Version Tag
```bash
# Start with initial version
git tag v0.1.0
```
## Usage Pattern
```bash
./build.sh # v0.1.0 → v0.1.1
./build.sh release # v0.1.1 → v0.1.2
./build.sh package # v0.1.2 → v0.1.3
```
## Manual Version Control
### Major/Minor Version Bumps
```bash
# For feature releases (minor bump)
git tag v0.2.0 # Next build: v0.2.1
# For breaking changes (major bump)
git tag v1.0.0 # Next build: v1.0.1
```
### Version Reset
```bash
# Delete incorrect tags (if needed)
git tag -d v0.2.1
git push origin --delete v0.2.1 # If pushed to remote
# Create correct base version
git tag v0.2.0
# Next build will create v0.2.1
```
## Example Build Script Template
```bash
#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
print_status() { echo -e "${BLUE}[INFO]${NC} $1"; }
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Insert increment_version function here
case "${1:-build}" in
build)
increment_version
print_status "Building project..."
# Your build commands
;;
clean)
print_status "Cleaning build artifacts..."
# Your clean commands
;;
test)
print_status "Running tests..."
# Your test commands (no version increment)
;;
release)
increment_version
print_status "Building release..."
# Your release commands
;;
*)
echo "Usage: $0 {build|clean|test|release}"
exit 1
;;
esac
```
## Benefits
1. **Zero maintenance** - No manual version editing
2. **Build traceability** - Every build has unique version + metadata
3. **Git integration** - Automatic version tags
4. **Language agnostic** - Adapt generation for any language
5. **CI/CD friendly** - Works in automated environments
6. **Rollback friendly** - Easy to revert to previous versions
## Troubleshooting
### Version Not Incrementing
- Ensure you're in a git repository
- Check that git tags exist: `git tag --list`
- Verify tag format matches `v*.*.*` pattern
### Tag Already Exists
If a tag already exists, the build continues with existing version:
```
[WARNING] Tag v0.2.1 already exists - using existing version
```
### Missing Git Information
If git is unavailable, version files show "unknown" for git hash and branch.

View File

@ -1,319 +0,0 @@
# NOSTR Core Library - Usage Guide
## Overview
The NOSTR Core Library (`libnostr_core`) is a self-contained, exportable C library for NOSTR protocol implementation. It requires **no external cryptographic dependencies** (no OpenSSL, no libwally) and can be easily integrated into other projects.
## Key Features
- **Self-Contained Crypto**: All cryptographic operations implemented from scratch
- **Zero External Dependencies**: Only requires standard C library, cJSON, and libwebsockets
- **Cross-Platform**: Builds on Linux, macOS, Windows (with appropriate toolchain)
- **NIP-06 Compliant**: Proper BIP39/BIP32 implementation for key derivation
- **Thread-Safe**: Core cryptographic functions are stateless and thread-safe
- **Easy Integration**: Simple C API with clear error handling
## File Structure for Export
### Core Library Files (Required)
```
libnostr_core/
├── nostr_core.h # Main public API header
├── nostr_core.c # Core implementation
├── nostr_crypto.h # Crypto implementation header
├── nostr_crypto.c # Self-contained crypto implementation
├── Makefile # Build configuration
└── cjson/ # JSON library (can be replaced with system cJSON)
├── cJSON.h
└── cJSON.c
```
### Optional Files
```
├── examples/ # Usage examples (helpful for integration)
├── LIBRARY_USAGE.md # This usage guide
├── SELF_CONTAINED_CRYPTO.md # Crypto implementation details
└── CROSS_PLATFORM_GUIDE.md # Platform-specific build notes
```
## Integration Methods
### Method 1: Static Library Integration
1. **Copy Required Files**:
```bash
cp nostr_core.h nostr_core.c nostr_crypto.h nostr_crypto.c /path/to/your/project/
cp -r cjson/ /path/to/your/project/
```
2. **Build Static Library**:
```bash
gcc -c -fPIC nostr_core.c nostr_crypto.c cjson/cJSON.c
ar rcs libnostr_core.a nostr_core.o nostr_crypto.o cJSON.o
```
3. **Link in Your Project**:
```bash
gcc your_project.c -L. -lnostr_core -lm -o your_project
```
### Method 2: Direct Source Integration
Simply include the source files directly in your project:
```c
// In your project
#include "nostr_core.h"
// Compile with:
// gcc your_project.c nostr_core.c nostr_crypto.c cjson/cJSON.c -lm
```
### Method 3: Shared Library Integration
1. **Build Shared Library**:
```bash
make libnostr_core.so
```
2. **Install System-Wide** (optional):
```bash
sudo cp libnostr_core.so /usr/local/lib/
sudo cp nostr_core.h /usr/local/include/
sudo ldconfig
```
3. **Use in Projects**:
```bash
gcc your_project.c -lnostr_core -lm
```
## Building the Library
### Using the Provided Build Script
The library includes an automated build script that handles all dependencies and creates a self-contained static library.
**IMPORTANT**: The build script must be run from within the `nostr_core_lib` directory:
```bash
# Correct usage:
cd nostr_core_lib
./build.sh
# If you need to use it from your project directory:
cd nostr_core_lib
./build.sh
cd ..
gcc your_app.c nostr_core_lib/libnostr_core_x64.a -lz -ldl -lpthread -lm -o your_app
```
**Common Error**: Running `./nostr_core_lib/build.sh` from outside the library directory will fail with:
```
[ERROR] Build script must be run from the nostr_core_lib directory
```
### Build Script Options
```bash
# Auto-detect NIPs from your source code
./build.sh
# Force specific NIPs
./build.sh --nips=1,6,19
# Build all available NIPs
./build.sh --nips=all
# Build for specific architecture
./build.sh arm64
# Build with tests
./build.sh --tests
# Get help
./build.sh --help
```
The build script automatically:
- Scans your `.c` files for `#include "nip*.h"` statements
- Compiles only the needed NIPs
- Creates a self-contained static library (`libnostr_core_x64.a`)
- Includes all dependencies (OpenSSL, curl, secp256k1, cJSON)
## Basic Usage Example
```c
#include "nostr_core.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
// Initialize the library
if (nostr_init() != NOSTR_SUCCESS) {
fprintf(stderr, "Failed to initialize NOSTR library\n");
return 1;
}
// Generate a keypair
unsigned char private_key[32];
unsigned char public_key[32];
if (nostr_generate_keypair(private_key, public_key) != NOSTR_SUCCESS) {
fprintf(stderr, "Failed to generate keypair\n");
nostr_cleanup();
return 1;
}
// Convert to hex and bech32
char private_hex[65], public_hex[65];
char nsec[100], npub[100];
nostr_bytes_to_hex(private_key, 32, private_hex);
nostr_bytes_to_hex(public_key, 32, public_hex);
nostr_key_to_bech32(private_key, "nsec", nsec);
nostr_key_to_bech32(public_key, "npub", npub);
printf("Private Key: %s\n", private_hex);
printf("Public Key: %s\n", public_hex);
printf("nsec: %s\n", nsec);
printf("npub: %s\n", npub);
// Create and sign an event
cJSON* event = nostr_create_text_event("Hello NOSTR!", private_key);
if (event) {
char* event_json = cJSON_Print(event);
printf("Signed Event: %s\n", event_json);
free(event_json);
cJSON_Delete(event);
}
// Cleanup
nostr_cleanup();
return 0;
}
```
## Dependency Management
### Required Dependencies
- **Standard C Library**: malloc, string functions, file I/O
- **Math Library**: `-lm` (for cryptographic calculations)
- **cJSON**: JSON parsing (included, or use system version)
### Optional Dependencies
- **libwebsockets**: Only needed for relay communication functions
- **System cJSON**: Can replace bundled version
### Minimal Integration (Crypto Only)
If you only need key generation and signing:
```bash
# Build with minimal dependencies
gcc -DNOSTR_CRYPTO_ONLY your_project.c nostr_crypto.c -lm
```
## Cross-Platform Considerations
### Linux
- Works out of the box with GCC
- Install build-essential: `sudo apt install build-essential`
### macOS
- Works with Xcode command line tools
- May need: `xcode-select --install`
### Windows
- Use MinGW-w64 or MSYS2
- Or integrate with Visual Studio project
### Embedded Systems
- Library is designed to work on resource-constrained systems
- No heap allocations in core crypto functions
- Stack usage is predictable and bounded
## API Reference
### Initialization
```c
int nostr_init(void); // Initialize library
void nostr_cleanup(void); // Cleanup resources
const char* nostr_strerror(int error); // Get error string
```
### Key Generation
```c
int nostr_generate_keypair(unsigned char* private_key, unsigned char* public_key);
int nostr_generate_mnemonic_and_keys(char* mnemonic, size_t mnemonic_size,
int account, unsigned char* private_key,
unsigned char* public_key);
int nostr_derive_keys_from_mnemonic(const char* mnemonic, int account,
unsigned char* private_key, unsigned char* public_key);
```
### Event Creation
```c
cJSON* nostr_create_text_event(const char* content, const unsigned char* private_key);
cJSON* nostr_create_profile_event(const char* name, const char* about,
const unsigned char* private_key);
int nostr_sign_event(cJSON* event, const unsigned char* private_key);
```
### Utilities
```c
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_key_to_bech32(const unsigned char* key, const char* hrp, char* output);
nostr_input_type_t nostr_detect_input_type(const char* input);
```
## Error Handling
All functions return standardized error codes:
- `NOSTR_SUCCESS` (0): Operation successful
- `NOSTR_ERROR_INVALID_INPUT` (-1): Invalid parameters
- `NOSTR_ERROR_CRYPTO_FAILED` (-2): Cryptographic operation failed
- `NOSTR_ERROR_MEMORY_FAILED` (-3): Memory allocation failed
- `NOSTR_ERROR_IO_FAILED` (-4): File I/O operation failed
- `NOSTR_ERROR_NETWORK_FAILED` (-5): Network operation failed
## Security Considerations
1. **Private Key Handling**: Always clear private keys from memory when done
2. **Random Number Generation**: Uses `/dev/urandom` on Unix systems
3. **Memory Safety**: All buffers are bounds-checked
4. **Constant-Time Operations**: Critical crypto operations are timing-attack resistant
## Testing Your Integration
Use the provided examples to verify your integration:
```bash
# Test key generation
./examples/simple_keygen
# Test mnemonic functionality
./examples/mnemonic_generation
# Test event creation
./examples/text_event
# Test all functionality
./examples/utility_functions
```
## Support and Documentation
- See `examples/` directory for comprehensive usage examples
- Check `SELF_CONTAINED_CRYPTO.md` for cryptographic implementation details
- Review `CROSS_PLATFORM_GUIDE.md` for platform-specific notes
- All functions are documented in `nostr_core.h`
## License
This library is designed to be freely integrable into other projects. Check the individual file headers for specific licensing information.
---
**Quick Start**: Copy `nostr_core.h`, `nostr_core.c`, `nostr_crypto.h`, `nostr_crypto.c`, and the `cjson/` folder to your project, then compile with `-lm`. That's it!

View File

@ -1,106 +0,0 @@
import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils'
import { secp256k1 } from '@noble/curves/secp256k1'
import { cbc } from '@noble/ciphers/aes'
import { base64 } from '@scure/base'
// UTF-8 encoder/decoder
const utf8Encoder = new TextEncoder()
const utf8Decoder = new TextDecoder()
function getNormalizedX(key) {
return key.slice(1, 33)
}
function encrypt(secretKey, pubkey, text) {
console.log(`[JS] Encrypting "${text}" using sk1 -> pk2`)
console.log(`[JS] Private Key: ${secretKey}`)
console.log(`[JS] Public Key: ${pubkey}`)
// Step 1: Get shared secret
const key = secp256k1.getSharedSecret(secretKey, '02' + pubkey)
console.log(`[JS] Shared Secret: ${bytesToHex(key)}`)
// Step 2: Normalize key (remove first byte, keep next 32)
const normalizedKey = getNormalizedX(key)
console.log(`[JS] Normalized Key: ${bytesToHex(normalizedKey)}`)
// Step 3: Generate random IV
const iv = randomBytes(16)
console.log(`[JS] IV: ${bytesToHex(iv)}`)
// Step 4: Encode plaintext to UTF-8
const plaintext = utf8Encoder.encode(text)
console.log(`[JS] UTF-8 Plaintext: ${bytesToHex(plaintext)}`)
// Step 5: AES-CBC encryption
const ciphertext = cbc(normalizedKey, iv).encrypt(plaintext)
console.log(`[JS] Raw Ciphertext: ${bytesToHex(new Uint8Array(ciphertext))}`)
// Step 6: Base64 encoding
const ctb64 = base64.encode(new Uint8Array(ciphertext))
const ivb64 = base64.encode(new Uint8Array(iv.buffer))
const result = `${ctb64}?iv=${ivb64}`
console.log(`[JS] Encrypted Result: ${result}`)
return result
}
function decrypt(secretKey, pubkey, data) {
console.log(`[JS] Decrypting "${data}" using sk2 + pk1`)
console.log(`[JS] Private Key: ${secretKey}`)
console.log(`[JS] Public Key: ${pubkey}`)
// Step 1: Parse format
const [ctb64, ivb64] = data.split('?iv=')
// Step 2: Get shared secret
const key = secp256k1.getSharedSecret(secretKey, '02' + pubkey)
console.log(`[JS] Shared Secret: ${bytesToHex(key)}`)
// Step 3: Normalize key
const normalizedKey = getNormalizedX(key)
console.log(`[JS] Normalized Key: ${bytesToHex(normalizedKey)}`)
// Step 4: Base64 decode
const iv = base64.decode(ivb64)
const ciphertext = base64.decode(ctb64)
console.log(`[JS] IV: ${bytesToHex(iv)}`)
console.log(`[JS] Raw Ciphertext: ${bytesToHex(ciphertext)}`)
// Step 5: AES-CBC decryption
const plaintext = cbc(normalizedKey, iv).decrypt(ciphertext)
console.log(`[JS] Decrypted Plaintext: ${bytesToHex(plaintext)}`)
// Step 6: UTF-8 decode
const result = utf8Decoder.decode(plaintext)
console.log(`[JS] UTF-8 Decoded: "${result}"`)
return result
}
// Test with exact vectors
async function main() {
console.log("=== NIP-04 DEBUG COMPARISON (JavaScript) ===")
const sk1 = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe"
const pk1 = "b38ce15d3d9874ee710dfabb7ff9801b1e0e20aace6e9a1a05fa7482a04387d1"
const sk2 = "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220"
const pk2 = "dcb33a629560280a0ee3b6b99b68c044fe8914ad8a984001ebf6099a9b474dc3"
const plaintext = "nanana"
const expectedCiphertext = "d6Joav5EciPI9hdHw31vmQ==?iv=fWs5rfv2+532arG/k83kcA=="
console.log("\n--- ENCRYPTION TEST ---")
const encrypted = encrypt(sk1, pk2, plaintext)
console.log("\n--- DECRYPTION TEST ---")
const decrypted = decrypt(sk2, pk1, expectedCiphertext)
console.log("\n--- RESULTS ---")
console.log(`Encryption Success: Generated ciphertext`)
console.log(`Decryption Success: ${decrypted === plaintext}`)
console.log(`Expected: "${plaintext}"`)
console.log(`Got: "${decrypted}"`)
}
main().catch(console.error)

View File

@ -1,280 +0,0 @@
import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils'
import { secp256k1 } from '@noble/curves/secp256k1'
import { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf'
import { hmac } from '@noble/hashes/hmac'
import { sha256 } from '@noble/hashes/sha256'
import { concatBytes } from '@noble/hashes/utils'
import { base64 } from '@scure/base'
import { chacha20 } from '@noble/ciphers/chacha'
import { equalBytes } from '@noble/ciphers/utils'
// UTF-8 encoder/decoder
const utf8Encoder = new TextEncoder()
const utf8Decoder = new TextDecoder()
const minPlaintextSize = 0x0001 // 1b msg => padded to 32b
const maxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb
function getConversationKey(privkeyA, pubkeyB) {
console.log(`[JS] Computing conversation key`)
console.log(`[JS] Private Key A: ${privkeyA}`)
console.log(`[JS] Public Key B: ${pubkeyB}`)
const sharedX = secp256k1.getSharedSecret(privkeyA, '02' + pubkeyB).subarray(1, 33)
console.log(`[JS] Shared Secret: ${bytesToHex(sharedX)}`)
const conversationKey = hkdf_extract(sha256, sharedX, 'nip44-v2')
console.log(`[JS] Conversation Key: ${bytesToHex(conversationKey)}`)
return conversationKey
}
function getMessageKeys(conversationKey, nonce) {
console.log(`[JS] Deriving message keys`)
console.log(`[JS] Conversation Key: ${bytesToHex(conversationKey)}`)
console.log(`[JS] Nonce: ${bytesToHex(nonce)}`)
const keys = hkdf_expand(sha256, conversationKey, nonce, 76)
const result = {
chacha_key: keys.subarray(0, 32),
chacha_nonce: keys.subarray(32, 44),
hmac_key: keys.subarray(44, 76),
}
console.log(`[JS] ChaCha20 Key: ${bytesToHex(result.chacha_key)}`)
console.log(`[JS] ChaCha20 Nonce: ${bytesToHex(result.chacha_nonce)}`)
console.log(`[JS] HMAC Key: ${bytesToHex(result.hmac_key)}`)
return result
}
function calcPaddedLen(len) {
if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')
if (len <= 32) return 32
const nextPower = 1 << (Math.floor(Math.log2(len - 1)) + 1)
const chunk = nextPower <= 256 ? 32 : nextPower / 8
return chunk * (Math.floor((len - 1) / chunk) + 1)
}
function writeU16BE(num) {
if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > maxPlaintextSize)
throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')
const arr = new Uint8Array(2)
new DataView(arr.buffer).setUint16(0, num, false)
return arr
}
function pad(plaintext) {
console.log(`[JS] Padding plaintext: "${plaintext}"`)
const unpadded = utf8Encoder.encode(plaintext)
const unpaddedLen = unpadded.length
console.log(`[JS] Unpadded length: ${unpaddedLen}`)
console.log(`[JS] Unpadded bytes: ${bytesToHex(unpadded)}`)
const prefix = writeU16BE(unpaddedLen)
console.log(`[JS] Length prefix: ${bytesToHex(prefix)}`)
const paddedLen = calcPaddedLen(unpaddedLen)
console.log(`[JS] Calculated padded length: ${paddedLen}`)
const suffix = new Uint8Array(paddedLen - unpaddedLen)
console.log(`[JS] Padding suffix length: ${suffix.length}`)
const result = concatBytes(prefix, unpadded, suffix)
console.log(`[JS] Final padded: ${bytesToHex(result)}`)
return result
}
function unpad(padded) {
console.log(`[JS] Unpadding data: ${bytesToHex(padded)}`)
const unpaddedLen = new DataView(padded.buffer).getUint16(0)
console.log(`[JS] Read length from prefix: ${unpaddedLen}`)
const unpadded = padded.subarray(2, 2 + unpaddedLen)
console.log(`[JS] Extracted unpadded: ${bytesToHex(unpadded)}`)
if (
unpaddedLen < minPlaintextSize ||
unpaddedLen > maxPlaintextSize ||
unpadded.length !== unpaddedLen ||
padded.length !== 2 + calcPaddedLen(unpaddedLen)
) {
console.log(`[JS] Padding validation failed:`)
console.log(`[JS] unpaddedLen: ${unpaddedLen} (should be ${minPlaintextSize}-${maxPlaintextSize})`)
console.log(`[JS] unpadded.length: ${unpadded.length}`)
console.log(`[JS] padded.length: ${padded.length}`)
console.log(`[JS] expected padded length: ${2 + calcPaddedLen(unpaddedLen)}`)
throw new Error('invalid padding')
}
const result = utf8Decoder.decode(unpadded)
console.log(`[JS] Decoded plaintext: "${result}"`)
return result
}
function hmacAad(key, message, aad) {
if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')
console.log(`[JS] Computing HMAC`)
console.log(`[JS] HMAC Key: ${bytesToHex(key)}`)
console.log(`[JS] AAD: ${bytesToHex(aad)}`)
console.log(`[JS] Message: ${bytesToHex(message)}`)
const combined = concatBytes(aad, message)
console.log(`[JS] Combined AAD+Message: ${bytesToHex(combined)}`)
const result = hmac(sha256, key, combined)
console.log(`[JS] HMAC Result: ${bytesToHex(result)}`)
return result
}
function decodePayload(payload) {
console.log(`[JS] Decoding payload: ${payload}`)
if (typeof payload !== 'string') throw new Error('payload must be a valid string')
const plen = payload.length
if (plen < 132 || plen > 87472) throw new Error('invalid payload length: ' + plen)
if (payload[0] === '#') throw new Error('unknown encryption version')
let data
try {
data = base64.decode(payload)
} catch (error) {
throw new Error('invalid base64: ' + error.message)
}
console.log(`[JS] Base64 decoded data: ${bytesToHex(data)}`)
const dlen = data.length
if (dlen < 99 || dlen > 65603) throw new Error('invalid data length: ' + dlen)
const vers = data[0]
if (vers !== 2) throw new Error('unknown encryption version ' + vers)
const result = {
nonce: data.subarray(1, 33),
ciphertext: data.subarray(33, -32),
mac: data.subarray(-32),
}
console.log(`[JS] Version: ${vers}`)
console.log(`[JS] Nonce: ${bytesToHex(result.nonce)}`)
console.log(`[JS] Ciphertext: ${bytesToHex(result.ciphertext)}`)
console.log(`[JS] MAC: ${bytesToHex(result.mac)}`)
return result
}
function encrypt(plaintext, conversationKey, nonce = randomBytes(32)) {
console.log(`[JS] ===== ENCRYPTING "${plaintext}" =====`)
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)
const padded = pad(plaintext)
console.log(`[JS] Encrypting with ChaCha20`)
const ciphertext = chacha20(chacha_key, chacha_nonce, padded)
console.log(`[JS] ChaCha20 ciphertext: ${bytesToHex(ciphertext)}`)
const mac = hmacAad(hmac_key, ciphertext, nonce)
console.log(`[JS] Building final payload`)
const payload = concatBytes(new Uint8Array([2]), nonce, ciphertext, mac)
console.log(`[JS] Raw payload: ${bytesToHex(payload)}`)
const result = base64.encode(payload)
console.log(`[JS] Base64 encoded result: ${result}`)
return result
}
function decrypt(payload, conversationKey) {
console.log(`[JS] ===== DECRYPTING "${payload}" =====`)
const { nonce, ciphertext, mac } = decodePayload(payload)
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)
const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)
console.log(`[JS] MAC verification`)
console.log(`[JS] Received MAC: ${bytesToHex(mac)}`)
console.log(`[JS] Calculated MAC: ${bytesToHex(calculatedMac)}`)
if (!equalBytes(calculatedMac, mac)) {
console.log(`[JS] MAC MISMATCH!`)
throw new Error('invalid MAC')
}
console.log(`[JS] MAC verification PASSED`)
console.log(`[JS] Decrypting with ChaCha20`)
const padded = chacha20(chacha_key, chacha_nonce, ciphertext)
console.log(`[JS] ChaCha20 decrypted: ${bytesToHex(padded)}`)
const result = unpad(padded)
console.log(`[JS] Final decrypted plaintext: "${result}"`)
return result
}
// Test with exact vectors that are failing in C
async function main() {
console.log("=== NIP-44 DEBUG COMPARISON (JavaScript) ===")
// Test vector 1: single char 'a' - MATCHES nostr-tools official vector
const test1 = {
sec1: "0000000000000000000000000000000000000000000000000000000000000001",
sec2: "0000000000000000000000000000000000000000000000000000000000000002",
plaintext: "a",
expectedPayload: "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb"
}
console.log("\n=== TEST 1: Single char 'a' ===")
// Step 1: Get conversation key using sender's private key + recipient's public key
const sec1Bytes = hexToBytes(test1.sec1)
const sec2Bytes = hexToBytes(test1.sec2)
const pub2 = bytesToHex(secp256k1.getPublicKey(sec2Bytes, false).subarray(1, 33)) // x-only
console.log(`[JS] Derived pub2 from sec2: ${pub2}`)
const conversationKey1 = getConversationKey(test1.sec1, pub2)
// Step 2: Try to decrypt the expected payload (this should work if our C code matches)
console.log("\n--- DECRYPTION TEST ---")
try {
const decrypted1 = decrypt(test1.expectedPayload, conversationKey1)
console.log(`✅ Decryption SUCCESS: "${decrypted1}"`)
console.log(`✅ Match: ${decrypted1 === test1.plaintext}`)
} catch (error) {
console.log(`❌ Decryption FAILED: ${error.message}`)
}
// Step 3: Generate fresh encryption to compare format
console.log("\n--- ENCRYPTION TEST (with random nonce) ---")
const encrypted1 = encrypt(test1.plaintext, conversationKey1)
console.log(`Generated payload: ${encrypted1}`)
// Step 4: Decrypt our own encryption (round-trip test)
console.log("\n--- ROUND-TRIP TEST ---")
try {
const roundTrip1 = decrypt(encrypted1, conversationKey1)
console.log(`✅ Round-trip SUCCESS: "${roundTrip1}"`)
console.log(`✅ Match: ${roundTrip1 === test1.plaintext}`)
} catch (error) {
console.log(`❌ Round-trip FAILED: ${error.message}`)
}
// Test the other failing vectors too
console.log("\n=== TEST 2: Emoji ===")
const test2 = {
sec1: "0000000000000000000000000000000000000000000000000000000000000002",
sec2: "0000000000000000000000000000000000000000000000000000000000000001",
plaintext: "🍕🫃",
expectedPayload: "AvAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAPSKSK6is9ngkX2+cSq85Th16oRTISAOfhStnixqZziKMDvB0QQzgFZdjLTPicCJaV8nDITO+QfaQ61+KbWQIOO2Yj"
}
const pub1 = bytesToHex(secp256k1.getPublicKey(hexToBytes(test2.sec2), false).subarray(1, 33))
const conversationKey2 = getConversationKey(test2.sec1, pub1)
try {
const decrypted2 = decrypt(test2.expectedPayload, conversationKey2)
console.log(`✅ Test 2 SUCCESS: "${decrypted2}"`)
} catch (error) {
console.log(`❌ Test 2 FAILED: ${error.message}`)
}
}
main().catch(console.error)

View File

@ -1,40 +0,0 @@
/*
* Example: Basic NOSTR functionality
* This example shows auto-detection working with selective includes
*/
#include "nostr_core/nip001.h" // Basic protocol
#include "nostr_core/nip006.h" // Key generation (will be created next)
#include "nostr_core/nip019.h" // Bech32 encoding (will be created next)
#include <stdio.h>
int main() {
printf("NOSTR Core Library - Basic Example\n");
// Initialize library
if (nostr_init() != 0) {
printf("Failed to initialize NOSTR library\n");
return 1;
}
printf("Library initialized successfully!\n");
// Generate keypair (from NIP-006)
// unsigned char private_key[32], public_key[32];
// nostr_generate_keypair(private_key, public_key);
// Convert to bech32 (from NIP-019)
// char nsec[100];
// nostr_key_to_bech32(private_key, "nsec", nsec);
// Create basic event (from NIP-001)
// cJSON* event = nostr_create_and_sign_event(1, "Hello Nostr!", NULL, private_key, 0);
printf("Example completed - the build script should detect NIPs: 001, 006, 019\n");
// Cleanup
nostr_cleanup();
return 0;
}

View File

@ -1,104 +0,0 @@
/*
* Example demonstrating modular NIP usage
* Shows how different NIPs can be used independently
*/
#include "nostr_core/nip001.h" // Basic protocol
#include "nostr_core/nip005.h" // NIP-05 DNS verification
#include "nostr_core/nip006.h" // Key derivation
#include "nostr_core/nip011.h" // Relay information
#include "nostr_core/nip013.h" // Proof of work
#include "nostr_core/nip019.h" // Bech32 encoding
#include "nostr_core/utils.h" // Utility functions
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("=== Modular NOSTR Core Library Demo ===\n\n");
// Initialize the library
if (nostr_init() != NOSTR_SUCCESS) {
printf("Failed to initialize NOSTR library\n");
return 1;
}
// Test NIP-006: Key generation and detection
printf("1. NIP-006: Key Generation and Input Detection\n");
unsigned char private_key[32], public_key[32];
if (nostr_generate_keypair(private_key, public_key) == NOSTR_SUCCESS) {
char private_hex[65], public_hex[65];
nostr_bytes_to_hex(private_key, 32, private_hex);
nostr_bytes_to_hex(public_key, 32, public_hex);
printf(" Generated keypair:\n");
printf(" Private: %s\n", private_hex);
printf(" Public: %s\n", public_hex);
// Test input type detection
nostr_input_type_t type = nostr_detect_input_type(private_hex);
printf(" Input type: %s\n",
type == NOSTR_INPUT_NSEC_HEX ? "NSEC_HEX" :
type == NOSTR_INPUT_NSEC_BECH32 ? "NSEC_BECH32" :
type == NOSTR_INPUT_MNEMONIC ? "MNEMONIC" : "UNKNOWN");
}
// Test NIP-019: Bech32 encoding
printf("\n2. NIP-019: Bech32 Encoding\n");
char bech32_nsec[200], bech32_npub[200];
if (nostr_key_to_bech32(private_key, "nsec", bech32_nsec) == NOSTR_SUCCESS) {
printf(" nsec: %s\n", bech32_nsec);
}
if (nostr_key_to_bech32(public_key, "npub", bech32_npub) == NOSTR_SUCCESS) {
printf(" npub: %s\n", bech32_npub);
}
// Test NIP-001: Event creation
printf("\n3. NIP-001: Event Creation\n");
cJSON* event = nostr_create_and_sign_event(1, "Hello from modular NOSTR!", NULL, private_key, 0);
if (event) {
char* event_json = cJSON_Print(event);
printf(" Created event: %s\n", event_json);
free(event_json);
cJSON_Delete(event);
}
// Test NIP-013: Proof of Work (light test)
printf("\n4. NIP-013: Proof of Work\n");
cJSON* pow_event = nostr_create_and_sign_event(1, "PoW test message", NULL, private_key, 0);
if (pow_event) {
printf(" Adding PoW (target difficulty: 4)...\n");
int result = nostr_add_proof_of_work(pow_event, private_key, 4, 100000, 10000, 5000, NULL, NULL);
if (result == NOSTR_SUCCESS) {
cJSON* id_item = cJSON_GetObjectItem(pow_event, "id");
if (id_item) {
printf(" PoW success! Event ID: %s\n", cJSON_GetStringValue(id_item));
}
} else {
printf(" PoW failed or timeout\n");
}
cJSON_Delete(pow_event);
}
// Test NIP-005: DNS verification (parse only - no network call in example)
printf("\n5. NIP-005: DNS Identifier Parsing\n");
const char* test_json = "{\"names\":{\"test\":\"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"}}";
char found_pubkey[65];
int result = nostr_nip05_parse_well_known(test_json, "test", found_pubkey, NULL, NULL);
if (result == NOSTR_SUCCESS) {
printf(" Parsed pubkey from test JSON: %s\n", found_pubkey);
}
// Test NIP-011: Just show the structure exists
printf("\n6. NIP-011: Relay Information Structure\n");
printf(" Relay info structure available for fetching relay metadata\n");
printf(" (Network calls not performed in this example)\n");
printf("\n=== All modular NIPs working! ===\n");
nostr_cleanup();
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +0,0 @@
#include "nip004.h"
#include "utils.h"
#include <stdio.h>
int main() {
printf("Testing crypto initialization...\n");
if (nostr_crypto_init() != 0) {
printf("❌ Crypto init failed\n");
return 1;
}
printf("✅ Crypto init successful\n");
// Test random bytes generation
unsigned char random_bytes[16];
if (nostr_secp256k1_get_random_bytes(random_bytes, 16) != 1) {
printf("❌ Random bytes generation failed\n");
return 1;
}
printf("✅ Random bytes generation successful\n");
// Test base64 encoding
char output[64];
size_t encoded_len = base64_encode(random_bytes, 16, output, sizeof(output));
if (encoded_len == 0) {
printf("❌ Base64 encoding failed\n");
return 1;
}
printf("✅ Base64 encoding successful: %s\n", output);
nostr_crypto_cleanup();
return 0;
}

View File

@ -1,69 +0,0 @@
const { generateSecretKey, getPublicKey, nip04 } = require('nostr-tools');
const { bytesToHex } = require('@noble/hashes/utils');
function generateTestVector(testName, plaintext) {
// Generate random keys
const sk1 = generateSecretKey();
const pk1 = getPublicKey(sk1);
const sk2 = generateSecretKey();
const pk2 = getPublicKey(sk2);
// Encrypt from Alice (sk1) to Bob (pk2)
const encrypted = nip04.encrypt(sk1, pk2, plaintext);
// Verify decryption works (Bob using sk2 to decrypt from Alice pk1)
const decrypted = nip04.decrypt(sk2, pk1, encrypted);
if (decrypted !== plaintext) {
throw new Error(`Decryption failed for ${testName}`);
}
return {
testName,
sk1: bytesToHex(sk1),
pk1: pk1,
sk2: bytesToHex(sk2),
pk2: pk2,
plaintext: plaintext,
encrypted: encrypted
};
}
console.log('=== NIP-04 Test Vector Generation ===\n');
// Generate 3 test vectors with different plaintexts
const testVectors = [
generateTestVector('TEST_VECTOR_2', 'Hello, NOSTR!'),
generateTestVector('TEST_VECTOR_3', 'This is a longer message to test encryption with more content. 🚀'),
generateTestVector('TEST_VECTOR_4', 'Short')
];
// Output in C format for easy integration
testVectors.forEach((vector, index) => {
console.log(`=== ${vector.testName}: ${vector.plaintext.replace(/\n/g, '\\n')} ===`);
console.log(`SK1 (Alice): ${vector.sk1}`);
console.log(`PK1 (Alice): ${vector.pk1}`);
console.log(`SK2 (Bob): ${vector.sk2}`);
console.log(`PK2 (Bob): ${vector.pk2}`);
console.log(`Plaintext: "${vector.plaintext}"`);
console.log(`Expected: ${vector.encrypted}`);
console.log('');
});
// Also output as C code that can be copied directly
console.log('=== C CODE FOR INTEGRATION ===\n');
testVectors.forEach((vector, index) => {
const testNum = index + 2; // Start from 2 since we already have TEST_VECTOR_1
console.log(` // ${vector.testName}: ${vector.plaintext.replace(/"/g, '\\"')}`);
console.log(` {`);
console.log(` "${vector.sk1}",`);
console.log(` "${vector.pk1}",`);
console.log(` "${vector.sk2}",`);
console.log(` "${vector.pk2}",`);
console.log(` "${vector.plaintext.replace(/"/g, '\\"')}",`);
console.log(` "${vector.encrypted}"`);
console.log(` }${index < testVectors.length - 1 ? ',' : ''}`);
});
console.log('\n=== Generation Complete ===');

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,249 +0,0 @@
/*
* NIP-44 Debug Test - Step-by-step comparison with nostr-tools vectors
*
* This test prints intermediate values for comparison with nostr-tools
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "../nostr_core/nostr_core.h"
static int hex_to_bytes(const char* hex, unsigned char* bytes, size_t len) {
if (strlen(hex) != len * 2) return -1;
for (size_t i = 0; i < len; i++) {
if (sscanf(hex + i * 2, "%2hhx", &bytes[i]) != 1) {
return -1;
}
}
return 0;
}
static void bytes_to_hex(const unsigned char* bytes, size_t len, char* hex) {
for (size_t i = 0; i < len; i++) {
sprintf(hex + i * 2, "%02x", bytes[i]);
}
hex[len * 2] = '\0';
}
// Test a specific vector from nostr-tools
static int test_vector_step_by_step(const char* name,
const char* sec1_hex,
const char* sec2_hex,
const char* expected_conversation_key_hex,
const char* nonce_hex,
const char* plaintext,
const char* expected_payload) {
printf("\n🔍 Testing Vector: %s\n", name);
printf("=====================================\n");
// Step 1: Parse keys
unsigned char sec1[32], sec2[32];
if (hex_to_bytes(sec1_hex, sec1, 32) != 0 || hex_to_bytes(sec2_hex, sec2, 32) != 0) {
printf("❌ Failed to parse private keys\n");
return -1;
}
printf("📝 sec1: %s\n", sec1_hex);
printf("📝 sec2: %s\n", sec2_hex);
// Step 2: Generate public keys
unsigned char pub1[32], pub2[32];
if (nostr_ec_public_key_from_private_key(sec1, pub1) != 0 ||
nostr_ec_public_key_from_private_key(sec2, pub2) != 0) {
printf("❌ Failed to derive public keys\n");
return -1;
}
char pub1_hex[65], pub2_hex[65];
bytes_to_hex(pub1, 32, pub1_hex);
bytes_to_hex(pub2, 32, pub2_hex);
printf("📝 pub1: %s\n", pub1_hex);
printf("📝 pub2: %s\n", pub2_hex);
// Step 3: Calculate ECDH shared secret (our raw implementation)
unsigned char shared_secret[32];
if (ecdh_shared_secret(sec1, pub2, shared_secret) != 0) {
printf("❌ Failed to compute ECDH shared secret\n");
return -1;
}
char shared_hex[65];
bytes_to_hex(shared_secret, 32, shared_hex);
printf("🔗 ECDH shared secret: %s\n", shared_hex);
// Step 4: Calculate conversation key using HKDF-extract
unsigned char conversation_key[32];
const char* salt_str = "nip44-v2";
if (nostr_hkdf_extract((const unsigned char*)salt_str, strlen(salt_str),
shared_secret, 32, conversation_key) != 0) {
printf("❌ Failed to derive conversation key\n");
return -1;
}
char conv_key_hex[65];
bytes_to_hex(conversation_key, 32, conv_key_hex);
printf("🗝️ Our conversation key: %s\n", conv_key_hex);
printf("🎯 Expected conv key: %s\n", expected_conversation_key_hex);
if (strcmp(conv_key_hex, expected_conversation_key_hex) == 0) {
printf("✅ Conversation key matches!\n");
} else {
printf("❌ Conversation key MISMATCH!\n");
return -1;
}
// Step 5: Parse nonce
unsigned char nonce[32];
if (hex_to_bytes(nonce_hex, nonce, 32) != 0) {
printf("❌ Failed to parse nonce\n");
return -1;
}
printf("🎲 Nonce: %s\n", nonce_hex);
// Step 6: Derive message keys using HKDF-expand
unsigned char message_keys[76]; // 32 chacha_key + 12 chacha_nonce + 32 hmac_key
if (nostr_hkdf_expand(conversation_key, 32, nonce, 32, message_keys, 76) != 0) {
printf("❌ Failed to derive message keys\n");
return -1;
}
char chacha_key_hex[65], chacha_nonce_hex[25], hmac_key_hex[65];
bytes_to_hex(message_keys, 32, chacha_key_hex);
bytes_to_hex(message_keys + 32, 12, chacha_nonce_hex);
bytes_to_hex(message_keys + 44, 32, hmac_key_hex);
printf("🔐 ChaCha key: %s\n", chacha_key_hex);
printf("🔐 ChaCha nonce: %s\n", chacha_nonce_hex);
printf("🔐 HMAC key: %s\n", hmac_key_hex);
// Step 7: Test encryption with known nonce
char our_payload[8192];
int encrypt_result = nostr_nip44_encrypt_with_nonce(sec1, pub2, plaintext, nonce, our_payload, sizeof(our_payload));
if (encrypt_result == NOSTR_SUCCESS) {
printf("🔒 Our payload: %s\n", our_payload);
printf("🎯 Expected payload: %s\n", expected_payload);
if (strcmp(our_payload, expected_payload) == 0) {
printf("✅ Payload matches perfectly!\n");
} else {
printf("❌ Payload MISMATCH!\n");
// Try to decrypt expected payload with our conversation key
printf("\n🔍 Debugging: Trying to decrypt expected payload...\n");
char decrypted[8192];
int decrypt_result = nostr_nip44_decrypt(sec2, pub1, expected_payload, decrypted, sizeof(decrypted));
if (decrypt_result == NOSTR_SUCCESS) {
printf("✅ Successfully decrypted expected payload!\n");
printf("📝 Decrypted text: \"%s\"\n", decrypted);
if (strcmp(decrypted, plaintext) == 0) {
printf("✅ Decrypted text matches original!\n");
} else {
printf("❌ Decrypted text doesn't match original!\n");
}
} else {
printf("❌ Failed to decrypt expected payload (error: %d)\n", decrypt_result);
}
}
} else {
printf("❌ Encryption failed with error: %d\n", encrypt_result);
return -1;
}
return 0;
}
int main() {
printf("🧪 NIP-44 Debug Test - Step-by-step Vector Comparison\n");
printf("======================================================\n");
// Initialize the library
if (nostr_init() != NOSTR_SUCCESS) {
printf("❌ Failed to initialize NOSTR library\n");
return 1;
}
// Test the simple "a" vector
test_vector_step_by_step(
"Single char 'a'",
"0000000000000000000000000000000000000000000000000000000000000001",
"0000000000000000000000000000000000000000000000000000000000000002",
"c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d",
"0000000000000000000000000000000000000000000000000000000000000001",
"a",
"AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb"
);
// Test the emoji vector
test_vector_step_by_step(
"Emoji 🍕🫃",
"0000000000000000000000000000000000000000000000000000000000000002",
"0000000000000000000000000000000000000000000000000000000000000001",
"c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d",
"f00000000000000000000000000000f00000000000000000000000000000000f",
"🍕🫃",
"AvAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAPSKSK6is9ngkX2+cSq85Th16oRTISAOfhStnixqZziKMDvB0QQzgFZdjLTPicCJaV8nDITO+QfaQ61+KbWQIOO2Yj"
);
// Test with get_message_keys test vector to verify HKDF-expand
printf("\n🔍 Testing get_message_keys vector from nostr-tools:\n");
printf("===========================================\n");
unsigned char conv_key[32];
if (hex_to_bytes("a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", conv_key, 32) == 0) {
unsigned char test_nonce[32];
if (hex_to_bytes("e1e6f880560d6d149ed83dcc7e5861ee62a5ee051f7fde9975fe5d25d2a02d72", test_nonce, 32) == 0) {
unsigned char message_keys[76];
if (nostr_hkdf_expand(conv_key, 32, test_nonce, 32, message_keys, 76) == 0) {
char chacha_key_hex[65], chacha_nonce_hex[25], hmac_key_hex[65];
bytes_to_hex(message_keys, 32, chacha_key_hex);
bytes_to_hex(message_keys + 32, 12, chacha_nonce_hex);
bytes_to_hex(message_keys + 44, 32, hmac_key_hex);
printf("📝 Conv key: a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54\n");
printf("📝 Nonce: e1e6f880560d6d149ed83dcc7e5861ee62a5ee051f7fde9975fe5d25d2a02d72\n");
printf("🔐 Our ChaCha key: %s\n", chacha_key_hex);
printf("🎯 Expected ChaCha key: f145f3bed47cb70dbeaac07f3a3fe683e822b3715edb7c4fe310829014ce7d76\n");
printf("🔐 Our ChaCha nonce: %s\n", chacha_nonce_hex);
printf("🎯 Expected ChaCha nonce: c4ad129bb01180c0933a160c\n");
printf("🔐 Our HMAC key: %s\n", hmac_key_hex);
printf("🎯 Expected HMAC key: 027c1db445f05e2eee864a0975b0ddef5b7110583c8c192de3732571ca5838c4\n");
if (strcmp(chacha_key_hex, "f145f3bed47cb70dbeaac07f3a3fe683e822b3715edb7c4fe310829014ce7d76") == 0) {
printf("✅ ChaCha key matches!\n");
} else {
printf("❌ ChaCha key MISMATCH!\n");
}
if (strcmp(chacha_nonce_hex, "c4ad129bb01180c0933a160c") == 0) {
printf("✅ ChaCha nonce matches!\n");
} else {
printf("❌ ChaCha nonce MISMATCH!\n");
}
if (strcmp(hmac_key_hex, "027c1db445f05e2eee864a0975b0ddef5b7110583c8c192de3732571ca5838c4") == 0) {
printf("✅ HMAC key matches!\n");
} else {
printf("❌ HMAC key MISMATCH!\n");
}
} else {
printf("❌ Failed to expand message keys\n");
}
} else {
printf("❌ Failed to parse test nonce\n");
}
} else {
printf("❌ Failed to parse conversation key\n");
}
nostr_cleanup();
printf("\n🏁 Debug test completed\n");
return 0;
}

View File

@ -1,255 +0,0 @@
/*
* NIP-44 Detailed Debug Test - Print every single intermediate step
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "../nostr_core/nostr_core.h"
static int hex_to_bytes(const char* hex, unsigned char* bytes, size_t len) {
if (strlen(hex) != len * 2) return -1;
for (size_t i = 0; i < len; i++) {
if (sscanf(hex + i * 2, "%2hhx", &bytes[i]) != 1) {
return -1;
}
}
return 0;
}
static void bytes_to_hex(const unsigned char* bytes, size_t len, char* hex) {
for (size_t i = 0; i < len; i++) {
sprintf(hex + i * 2, "%02x", bytes[i]);
}
hex[len * 2] = '\0';
}
static void print_bytes(const char* label, const unsigned char* bytes, size_t len) {
char hex[len * 2 + 1];
bytes_to_hex(bytes, len, hex);
printf("%s: %s\n", label, hex);
}
// Test NIP-44 padding calculation (replicate from our crypto.c)
static size_t calc_padded_len(size_t unpadded_len) {
if (unpadded_len <= 32) {
return 32;
}
size_t next_power = 1;
while (next_power < unpadded_len) {
next_power <<= 1;
}
size_t chunk = (next_power <= 256) ? 32 : (next_power / 8);
return chunk * ((unpadded_len - 1) / chunk + 1);
}
// Test NIP-44 padding (replicate from our crypto.c)
static unsigned char* pad_plaintext_debug(const char* plaintext, size_t* padded_len) {
size_t unpadded_len = strlen(plaintext);
if (unpadded_len > 65535) {
return NULL;
}
printf("🔍 PADDING DEBUG:\n");
printf(" unpadded_len: %zu\n", unpadded_len);
*padded_len = calc_padded_len(unpadded_len + 2); // +2 for length prefix
printf(" calculated_padded_len: %zu\n", *padded_len);
unsigned char* padded = malloc(*padded_len);
if (!padded) return NULL;
// Write length prefix (big-endian u16)
padded[0] = (unpadded_len >> 8) & 0xFF;
padded[1] = unpadded_len & 0xFF;
printf(" length_prefix: %02x%02x (big-endian u16 = %zu)\n", padded[0], padded[1], unpadded_len);
// Copy plaintext (if any)
if (unpadded_len > 0) {
memcpy(padded + 2, plaintext, unpadded_len);
}
// Zero-fill padding
memset(padded + 2 + unpadded_len, 0, *padded_len - 2 - unpadded_len);
print_bytes(" padded_plaintext", padded, *padded_len);
return padded;
}
int main() {
printf("🧪 NIP-44 DETAILED DEBUG TEST\n");
printf("===============================\n\n");
// Initialize the library
if (nostr_init() != NOSTR_SUCCESS) {
printf("❌ Failed to initialize NOSTR library\n");
return 1;
}
printf("=== TESTING: Single char 'a' ===\n");
// Step 1: Parse private keys
unsigned char sec1[32], sec2[32];
hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000001", sec1, 32);
hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000002", sec2, 32);
print_bytes("sec1", sec1, 32);
print_bytes("sec2", sec2, 32);
// Step 2: Generate public keys
unsigned char pub1[32], pub2[32];
nostr_ec_public_key_from_private_key(sec1, pub1);
nostr_ec_public_key_from_private_key(sec2, pub2);
print_bytes("pub1", pub1, 32);
print_bytes("pub2", pub2, 32);
// Step 3: ECDH shared secret
unsigned char shared_secret[32];
ecdh_shared_secret(sec1, pub2, shared_secret);
print_bytes("ecdh_shared_secret", shared_secret, 32);
// Step 4: HKDF Extract (conversation key)
unsigned char conversation_key[32];
const char* salt_str = "nip44-v2";
nostr_hkdf_extract((const unsigned char*)salt_str, strlen(salt_str), shared_secret, 32, conversation_key);
print_bytes("conversation_key", conversation_key, 32);
// Step 5: Parse nonce
unsigned char nonce[32];
hex_to_bytes("0000000000000000000000000000000000000000000000000000000000000001", nonce, 32);
print_bytes("nonce", nonce, 32);
// Step 6: HKDF Expand (message keys)
unsigned char message_keys[76];
nostr_hkdf_expand(conversation_key, 32, nonce, 32, message_keys, 76);
print_bytes("chacha_key", message_keys, 32);
print_bytes("chacha_nonce", message_keys + 32, 12);
print_bytes("hmac_key", message_keys + 44, 32);
// Step 7: Pad plaintext
const char* plaintext = "a";
printf("\n🔍 PLAINTEXT: \"%s\" (length: %zu)\n", plaintext, strlen(plaintext));
size_t padded_len;
unsigned char* padded_plaintext = pad_plaintext_debug(plaintext, &padded_len);
if (!padded_plaintext) {
printf("❌ Failed to pad plaintext\n");
return 1;
}
// Step 8: ChaCha20 encrypt
printf("\n🔍 CHACHA20 ENCRYPTION:\n");
unsigned char* ciphertext = malloc(padded_len);
if (!ciphertext) {
printf("❌ Failed to allocate ciphertext buffer\n");
free(padded_plaintext);
return 1;
}
// Use our ChaCha20 function
if (chacha20_encrypt(message_keys, 0, message_keys + 32, padded_plaintext, ciphertext, padded_len) != 0) {
printf("❌ ChaCha20 encryption failed\n");
free(padded_plaintext);
free(ciphertext);
return 1;
}
print_bytes(" ciphertext", ciphertext, padded_len);
// Step 9: HMAC with AAD
printf("\n🔍 HMAC CALCULATION:\n");
unsigned char* aad_data = malloc(32 + padded_len);
if (!aad_data) {
printf("❌ Failed to allocate AAD buffer\n");
free(padded_plaintext);
free(ciphertext);
return 1;
}
memcpy(aad_data, nonce, 32);
memcpy(aad_data + 32, ciphertext, padded_len);
print_bytes(" aad_data", aad_data, 32 + padded_len);
unsigned char mac[32];
nostr_hmac_sha256(message_keys + 44, 32, aad_data, 32 + padded_len, mac);
print_bytes(" mac", mac, 32);
// Step 10: Construct final payload
printf("\n🔍 PAYLOAD CONSTRUCTION:\n");
size_t payload_len = 1 + 32 + padded_len + 32;
unsigned char* payload = malloc(payload_len);
if (!payload) {
printf("❌ Failed to allocate payload buffer\n");
free(padded_plaintext);
free(ciphertext);
free(aad_data);
return 1;
}
payload[0] = 0x02; // NIP-44 version 2
memcpy(payload + 1, nonce, 32);
memcpy(payload + 33, ciphertext, padded_len);
memcpy(payload + 33 + padded_len, mac, 32);
printf(" version: 0x%02x\n", payload[0]);
print_bytes(" payload_nonce", payload + 1, 32);
print_bytes(" payload_ciphertext", payload + 33, padded_len);
print_bytes(" payload_mac", payload + 33 + padded_len, 32);
print_bytes(" raw_payload", payload, payload_len);
// Step 11: Base64 encode
printf("\n🔍 BASE64 ENCODING:\n");
size_t b64_len = ((payload_len + 2) / 3) * 4 + 1;
char* base64_output = malloc(b64_len);
if (!base64_output) {
printf("❌ Failed to allocate base64 buffer\n");
free(padded_plaintext);
free(ciphertext);
free(aad_data);
free(payload);
return 1;
}
// Use our internal base64_encode function from crypto.c
extern size_t base64_encode(const unsigned char* data, size_t len, char* output, size_t output_size);
size_t actual_b64_len = base64_encode(payload, payload_len, base64_output, b64_len);
printf(" payload_length: %zu\n", payload_len);
printf(" base64_length: %zu\n", actual_b64_len);
printf(" our_base64: %s\n", base64_output);
printf(" expected: AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb\n");
if (strcmp(base64_output, "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb") == 0) {
printf("✅ PERFECT MATCH!\n");
} else {
printf("❌ MISMATCH - need to investigate!\n");
// Let's also try our full encrypt function for comparison
printf("\n🔍 FULL ENCRYPT FUNCTION TEST:\n");
char full_encrypt_output[8192];
int result = nostr_nip44_encrypt_with_nonce(sec1, pub2, plaintext, nonce, full_encrypt_output, sizeof(full_encrypt_output));
if (result == NOSTR_SUCCESS) {
printf(" full_encrypt: %s\n", full_encrypt_output);
} else {
printf(" full_encrypt failed with error: %d\n", result);
}
}
// Cleanup
free(padded_plaintext);
free(ciphertext);
free(aad_data);
free(payload);
free(base64_output);
nostr_cleanup();
printf("\n🏁 Detailed debug test completed\n");
return 0;
}

View File

@ -1,118 +0,0 @@
/*
* Simple NIP-44 Test
* Basic functionality test for NIP-44 encryption/decryption
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../nostr_core/nostr_core.h"
int main() {
printf("🧪 Simple NIP-44 Test\n");
printf("=====================\n\n");
// Initialize the library
if (nostr_init() != NOSTR_SUCCESS) {
printf("❌ Failed to initialize NOSTR library\n");
return 1;
}
// Test keys (from successful NIP-04 test)
const char* sender_key_hex = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe";
const char* recipient_key_hex = "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220";
unsigned char sender_private_key[32];
unsigned char recipient_private_key[32];
unsigned char recipient_public_key[32];
// Parse keys
if (nostr_hex_to_bytes(sender_key_hex, sender_private_key, 32) != 0) {
printf("❌ Failed to parse sender private key\n");
nostr_cleanup();
return 1;
}
if (nostr_hex_to_bytes(recipient_key_hex, recipient_private_key, 32) != 0) {
printf("❌ Failed to parse recipient private key\n");
nostr_cleanup();
return 1;
}
// Generate recipient's public key
if (nostr_ec_public_key_from_private_key(recipient_private_key, recipient_public_key) != 0) {
printf("❌ Failed to generate recipient public key\n");
nostr_cleanup();
return 1;
}
printf("✅ Keys parsed successfully\n");
// Test message
const char* test_message = "Hello, NIP-44! This is a test message.";
printf("📝 Test message: \"%s\"\n", test_message);
// Test encryption
char encrypted[8192];
printf("🔐 Testing NIP-44 encryption...\n");
int encrypt_result = nostr_nip44_encrypt(
sender_private_key,
recipient_public_key,
test_message,
encrypted,
sizeof(encrypted)
);
if (encrypt_result != NOSTR_SUCCESS) {
printf("❌ NIP-44 encryption failed with error: %d\n", encrypt_result);
nostr_cleanup();
return 1;
}
printf("✅ NIP-44 encryption successful!\n");
printf("📦 Encrypted length: %zu bytes\n", strlen(encrypted));
printf("📦 First 80 chars: %.80s...\n", encrypted);
// Test decryption
char decrypted[8192];
unsigned char sender_public_key[32];
// Generate sender's public key for decryption
if (nostr_ec_public_key_from_private_key(sender_private_key, sender_public_key) != 0) {
printf("❌ Failed to generate sender public key\n");
nostr_cleanup();
return 1;
}
printf("🔓 Testing NIP-44 decryption...\n");
int decrypt_result = nostr_nip44_decrypt(
recipient_private_key,
sender_public_key,
encrypted,
decrypted,
sizeof(decrypted)
);
if (decrypt_result != NOSTR_SUCCESS) {
printf("❌ NIP-44 decryption failed with error: %d\n", decrypt_result);
nostr_cleanup();
return 1;
}
printf("✅ NIP-44 decryption successful!\n");
printf("📝 Decrypted: \"%s\"\n", decrypted);
// Verify round-trip
if (strcmp(test_message, decrypted) == 0) {
printf("✅ Round-trip successful! Messages match perfectly.\n");
printf("\n🎉 NIP-44 TEST PASSED! 🎉\n");
nostr_cleanup();
return 0;
} else {
printf("❌ Round-trip failed! Messages don't match.\n");
printf("📝 Original: \"%s\"\n", test_message);
printf("📝 Decrypted: \"%s\"\n", decrypted);
nostr_cleanup();
return 1;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.