clean up
This commit is contained in:
parent
3ebfdc06c0
commit
1da4f6751e
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
319
LIBRARY_USAGE.md
319
LIBRARY_USAGE.md
|
@ -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!
|
|
@ -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)
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
2579
rfc8439.txt
2579
rfc8439.txt
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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 ===');
|
BIN
tests/bip32_test
BIN
tests/bip32_test
Binary file not shown.
Binary file not shown.
BIN
tests/http_test
BIN
tests/http_test
Binary file not shown.
Binary file not shown.
BIN
tests/nip04_test
BIN
tests/nip04_test
Binary file not shown.
BIN
tests/nip05_test
BIN
tests/nip05_test
Binary file not shown.
BIN
tests/nip11_test
BIN
tests/nip11_test
Binary file not shown.
BIN
tests/nip44_test
BIN
tests/nip44_test
Binary file not shown.
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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.
BIN
tests/wss_test
BIN
tests/wss_test
Binary file not shown.
Loading…
Reference in New Issue