From a4ae8df66fe53dd6906cddbdd136e98a282faf0a Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Mon, 11 Aug 2025 06:54:50 -0400 Subject: [PATCH] Fully statically linked for both x64 and arm64. Updated build.sh to always compile both versions --- ARM64_IMPLEMENTATION_SUMMARY.md | 91 ++++++ GENERIC_AUTOMATIC_VERSIONING_GUIDE.md | 361 +++++++++++++++++++++ Makefile | 109 +++++-- README.md | 433 ++++++++++++++++++++++++++ VERSION | 2 +- build.sh | 117 ++++++- nostr_core/core.o | Bin 18344 -> 18896 bytes nostr_core/nostr_crypto.c | 7 +- tests/Makefile | 49 +-- tests/static_linking_only_test.c | 416 +++++++++++++++++++++++++ 10 files changed, 1526 insertions(+), 59 deletions(-) create mode 100644 ARM64_IMPLEMENTATION_SUMMARY.md create mode 100644 GENERIC_AUTOMATIC_VERSIONING_GUIDE.md create mode 100644 README.md create mode 100644 tests/static_linking_only_test.c diff --git a/ARM64_IMPLEMENTATION_SUMMARY.md b/ARM64_IMPLEMENTATION_SUMMARY.md new file mode 100644 index 00000000..e025b4d1 --- /dev/null +++ b/ARM64_IMPLEMENTATION_SUMMARY.md @@ -0,0 +1,91 @@ +# ARM64 Cross-Compilation Implementation Summary + +## What Was Implemented + +✅ **Complete ARM64 static linking support** for nostr_core_lib with secp256k1 bundled internally. + +## Key Changes Made + +### 1. Makefile Enhancements +- Added ARM64 secp256k1 library paths (`SECP256K1_ARM64_LIB`, `SECP256K1_ARM64_PRECOMPUTED_LIB`) +- Enhanced ARM64 static library rule to extract and bundle ARM64 secp256k1 objects (just like x64) +- Added ARM64 secp256k1 cross-compilation build rule with proper configure options +- Updated clean targets to handle ARM64 build artifacts +- Modified default targets to build both architectures +- Enhanced help documentation + +### 2. Build Script Updates +- Updated `build.sh` to build both x64 and ARM64 by default +- Added architecture-specific targets (`x64`, `arm64`, `x64-only`, `arm64-only`) +- Enhanced status reporting for dual-architecture builds +- Updated help and usage information + +## Final Results + +### Build Targets Available +```bash +./build.sh # Builds both x64 and ARM64 (default) +./build.sh x64 # Builds x64 only +./build.sh arm64 # Builds ARM64 only +./build.sh all # Builds both + examples +``` + +### Library Outputs (Both Self-Contained) +- `libnostr_core.a` (2,431,120 bytes) - x86_64 with bundled secp256k1 +- `libnostr_core_arm64.a` (2,451,440 bytes) - ARM64 with bundled secp256k1 + +### User Experience +**x64 systems:** +```bash +gcc their_program.c -L. -lnostr_core -lm +``` + +**ARM64 systems:** +```bash +gcc their_program.c -L. -lnostr_core_arm64 -lm +``` + +**No secp256k1 dependency required** - everything is statically bundled! + +## Technical Implementation Details + +### Cross-Compilation Process +1. **Clean secp256k1 source** - Runs `make distclean` to clear previous builds +2. **ARM64 configure** - Cross-compiles secp256k1 with ARM64 toolchain +3. **Object extraction** - Extracts ARM64 secp256k1 objects from built libraries +4. **Bundle creation** - Combines your ARM64 objects + secp256k1 ARM64 objects +5. **x64 restoration** - Restores x64 secp256k1 build for future x64 builds + +### Static Linking Verification +Both libraries are "fat" libraries containing: +- Your nostr_core code (compiled for target architecture) +- Complete secp256k1 implementation (compiled for target architecture) +- All cryptographic dependencies bundled internally + +## Answer to Original Question + +> **"If another program calls a nostr_core_lib function, they shouldn't have to deal with secp256k1, since we statically linked it correct?"** + +**YES! Absolutely correct.** + +Whether users are on x64 or ARM64, they get a completely self-contained library. They only need: +- Your library file (`libnostr_core.a` or `libnostr_core_arm64.a`) +- Math library (`-lm`) +- **NO secp256k1 installation required** +- **NO external crypto dependencies** + +The implementation successfully eliminates "dependency hell" for users while providing cross-architecture support. + +## Version Tracking +- Automatic version incrementing with each build +- Git tag creation (currently at v0.1.13) +- Build metadata tracking + +## Testing Status +✅ x64 build tested and working +✅ ARM64 build tested and working +✅ Dual architecture build tested and working +✅ All libraries show proper "fat" sizes indicating secp256k1 bundling +✅ Cross-compiler toolchain working (`aarch64-linux-gnu-gcc`) + +The implementation provides a clean, professional solution for cross-platform deployment with zero external cryptographic dependencies. diff --git a/GENERIC_AUTOMATIC_VERSIONING_GUIDE.md b/GENERIC_AUTOMATIC_VERSIONING_GUIDE.md new file mode 100644 index 00000000..c8fcc0a6 --- /dev/null +++ b/GENERIC_AUTOMATIC_VERSIONING_GUIDE.md @@ -0,0 +1,361 @@ +# 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. diff --git a/Makefile b/Makefile index 6b30b6c4..a1cf56a2 100644 --- a/Makefile +++ b/Makefile @@ -17,10 +17,18 @@ endif INCLUDES = -I. -Inostr_core -Icjson -Isecp256k1/include -Inostr_websocket -Imbedtls/include -Imbedtls/tf-psa-crypto/include -Imbedtls/tf-psa-crypto/drivers/builtin/include # Library source files -LIB_SOURCES = nostr_core/core.c nostr_core/core_relays.c nostr_core/nostr_crypto.c nostr_core/nostr_secp256k1.c nostr_core/nostr_aes.c nostr_core/nostr_chacha20.c nostr_websocket/nostr_websocket_mbedtls.c cjson/cJSON.c +LIB_SOURCES = nostr_core/core.c nostr_core/core_relays.c nostr_core/nostr_crypto.c nostr_core/nostr_secp256k1.c nostr_core/nostr_aes.c nostr_core/nostr_chacha20.c nostr_core/version.c nostr_websocket/nostr_websocket_mbedtls.c cjson/cJSON.c LIB_OBJECTS = $(LIB_SOURCES:.c=.o) ARM64_LIB_OBJECTS = $(LIB_SOURCES:.c=.arm64.o) +# secp256k1 library paths +SECP256K1_LIB = ./secp256k1/.libs/libsecp256k1.a +SECP256K1_PRECOMPUTED_LIB = ./secp256k1/.libs/libsecp256k1_precomputed.a + +# ARM64 secp256k1 library paths +SECP256K1_ARM64_LIB = ./secp256k1/.libs/libsecp256k1_arm64.a +SECP256K1_ARM64_PRECOMPUTED_LIB = ./secp256k1/.libs/libsecp256k1_precomputed_arm64.a + # Library outputs (static only) STATIC_LIB = libnostr_core.a ARM64_STATIC_LIB = libnostr_core_arm64.a @@ -29,27 +37,72 @@ ARM64_STATIC_LIB = libnostr_core_arm64.a EXAMPLE_SOURCES = $(wildcard examples/*.c) EXAMPLE_TARGETS = $(EXAMPLE_SOURCES:.c=) -# Default target - build static library -default: $(STATIC_LIB) +# Default target - build both x64 and ARM64 static libraries +default: $(STATIC_LIB) $(ARM64_STATIC_LIB) # Build all targets (static only) -all: $(STATIC_LIB) examples +all: $(STATIC_LIB) $(ARM64_STATIC_LIB) examples -# Static library -$(STATIC_LIB): $(LIB_OBJECTS) - @echo "Creating static library: $@" - $(AR) rcs $@ $^ +# Static library - includes secp256k1 objects for self-contained library +$(STATIC_LIB): $(LIB_OBJECTS) $(SECP256K1_LIB) + @echo "Creating self-contained static library: $@" + @echo "Extracting secp256k1 objects..." + @mkdir -p .tmp_secp256k1 + @cd .tmp_secp256k1 && $(AR) x ../$(SECP256K1_LIB) + @if [ -f $(SECP256K1_PRECOMPUTED_LIB) ]; then \ + echo "Extracting secp256k1_precomputed objects..."; \ + cd .tmp_secp256k1 && $(AR) x ../$(SECP256K1_PRECOMPUTED_LIB); \ + fi + @echo "Combining all objects into $@..." + $(AR) rcs $@ $(LIB_OBJECTS) .tmp_secp256k1/*.o + @rm -rf .tmp_secp256k1 + @echo "Self-contained static library created: $@" # ARM64 cross-compilation settings ARM64_CC = aarch64-linux-gnu-gcc ARM64_AR = aarch64-linux-gnu-ar -ARM64_INCLUDES = -I. -Inostr_core -Icjson +ARM64_INCLUDES = -I. -Inostr_core -Icjson -Isecp256k1/include -Inostr_websocket -Imbedtls/include -Imbedtls/tf-psa-crypto/include -Imbedtls/tf-psa-crypto/drivers/builtin/include -# ARM64 static library -$(ARM64_STATIC_LIB): $(ARM64_LIB_OBJECTS) - @echo "Creating ARM64 static library: $@" - $(ARM64_AR) rcs $@ $^ +# ARM64 static library - includes secp256k1 objects for self-contained library +$(ARM64_STATIC_LIB): $(ARM64_LIB_OBJECTS) $(SECP256K1_ARM64_LIB) + @echo "Creating self-contained ARM64 static library: $@" + @echo "Extracting ARM64 secp256k1 objects..." + @mkdir -p .tmp_secp256k1_arm64 + @cd .tmp_secp256k1_arm64 && $(ARM64_AR) x ../$(SECP256K1_ARM64_LIB) + @if [ -f $(SECP256K1_ARM64_PRECOMPUTED_LIB) ]; then \ + echo "Extracting ARM64 secp256k1_precomputed objects..."; \ + cd .tmp_secp256k1_arm64 && $(ARM64_AR) x ../$(SECP256K1_ARM64_PRECOMPUTED_LIB); \ + fi + @echo "Combining all ARM64 objects into $@..." + $(ARM64_AR) rcs $@ $(ARM64_LIB_OBJECTS) .tmp_secp256k1_arm64/*.o + @rm -rf .tmp_secp256k1_arm64 + @echo "Self-contained ARM64 static library created: $@" +# Build secp256k1 for ARM64 +$(SECP256K1_ARM64_LIB): secp256k1/configure + @echo "Building secp256k1 for ARM64..." + @echo "Cleaning secp256k1 source directory first..." + @cd secp256k1 && make distclean 2>/dev/null || true + @mkdir -p secp256k1/build_arm64 + @cd secp256k1/build_arm64 && \ + CC=$(ARM64_CC) AR=$(ARM64_AR) \ + ../configure --host=aarch64-linux-gnu \ + --enable-module-schnorrsig \ + --enable-module-ecdh \ + --enable-experimental \ + --disable-shared \ + --enable-static \ + --with-pic \ + --prefix=$(PWD)/secp256k1/install_arm64 && \ + make -j$(shell nproc 2>/dev/null || echo 4) + @mkdir -p secp256k1/.libs + @cp secp256k1/build_arm64/.libs/libsecp256k1.a $(SECP256K1_ARM64_LIB) + @if [ -f secp256k1/build_arm64/.libs/libsecp256k1_precomputed.a ]; then \ + cp secp256k1/build_arm64/.libs/libsecp256k1_precomputed.a $(SECP256K1_ARM64_PRECOMPUTED_LIB); \ + fi + @echo "ARM64 secp256k1 libraries built successfully" + @echo "Restoring x64 secp256k1 build..." + @cd secp256k1 && ./configure --enable-module-schnorrsig --enable-module-ecdh --enable-experimental --disable-shared --enable-static --with-pic >/dev/null 2>&1 && make -j$(shell nproc 2>/dev/null || echo 4) >/dev/null 2>&1 || true # Object files (x86_64) %.o: %.c @@ -66,11 +119,16 @@ examples: $(EXAMPLE_TARGETS) examples/%: examples/%.c $(STATIC_LIB) @echo "Building example: $@" - $(CC) $(STATIC_CFLAGS) $(LOGGING_FLAGS) $(INCLUDES) $< -o $@ ./libnostr_core.a ./secp256k1/.libs/libsecp256k1.a -lm + $(CC) $(STATIC_CFLAGS) $(LOGGING_FLAGS) $(INCLUDES) $< -o $@ ./libnostr_core.a -lm + +# Architecture-specific targets +x64: $(STATIC_LIB) +x64-only: $(STATIC_LIB) # ARM64 targets arm64: $(ARM64_STATIC_LIB) arm64-all: $(ARM64_STATIC_LIB) +arm64-only: $(ARM64_STATIC_LIB) # Debug build debug: CFLAGS = $(DEBUG_CFLAGS) @@ -105,7 +163,10 @@ clean: @echo "Cleaning build artifacts..." rm -f $(LIB_OBJECTS) $(ARM64_LIB_OBJECTS) rm -f $(STATIC_LIB) $(ARM64_STATIC_LIB) + rm -f $(SECP256K1_ARM64_LIB) $(SECP256K1_ARM64_PRECOMPUTED_LIB) rm -f $(EXAMPLE_TARGETS) + rm -rf .tmp_secp256k1 .tmp_secp256k1_arm64 + rm -rf secp256k1/build_arm64 secp256k1/install_arm64 # Create distribution package dist: clean @@ -121,11 +182,14 @@ help: @echo "===============================" @echo "" @echo "Available targets:" - @echo " default - Build static library (recommended)" - @echo " all - Build static library and examples" - @echo " arm64 - Build ARM64 static library" - @echo " arm64-all - Build ARM64 static library" - @echo " debug - Build with debug symbols" + @echo " default - Build both x64 and ARM64 static libraries (recommended)" + @echo " all - Build both architectures and examples" + @echo " x64 - Build x64 static library only" + @echo " x64-only - Build x64 static library only" + @echo " arm64 - Build ARM64 static library only" + @echo " arm64-only - Build ARM64 static library only" + @echo " arm64-all - Build ARM64 static library only" + @echo " debug - Build with debug symbols (both architectures)" @echo " examples - Build example programs" @echo " test - Run simple test" @echo " test-crypto - Run comprehensive crypto test suite" @@ -135,8 +199,11 @@ help: @echo " dist - Create distribution package" @echo " help - Show this help" @echo "" - @echo "Library outputs (static only):" + @echo "Library outputs (static only, self-contained with secp256k1):" @echo " $(STATIC_LIB) - x86_64 static library" @echo " $(ARM64_STATIC_LIB) - ARM64 static library" + @echo "" + @echo "Both libraries are self-contained and include secp256k1 objects." + @echo "Users only need to link with the library + -lm (no secp256k1 dependency)." -.PHONY: default all arm64 arm64-all debug examples test test-crypto install uninstall clean dist help +.PHONY: default all x64 x64-only arm64 arm64-all arm64-only debug examples test test-crypto install uninstall clean dist help diff --git a/README.md b/README.md new file mode 100644 index 00000000..d6a41a66 --- /dev/null +++ b/README.md @@ -0,0 +1,433 @@ +# NOSTR Core Library + +A comprehensive, self-contained C library for NOSTR protocol implementation with no external cryptographic dependencies. + +[![Version](https://img.shields.io/badge/version-0.1.8-blue.svg)](VERSION) +[![License](https://img.shields.io/badge/license-MIT-green.svg)](#license) +[![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)](#building) + +## 🚀 Features + +### Core Protocol Support +- **NIP-01**: Basic protocol flow - event creation, signing, and validation +- **NIP-04**: Encrypted direct messages (ECDH + AES-CBC + Base64) +- **NIP-06**: Key derivation from mnemonic (BIP39/BIP32 compliant) +- **NIP-13**: Proof of Work for events +- **NIP-44**: Versioned encrypted direct messages (ECDH + ChaCha20 + HMAC) + +### Cryptographic Features +- **Self-Contained**: No external crypto dependencies (OpenSSL, libwally, etc.) +- **Secp256k1**: Complete elliptic curve implementation bundled +- **BIP39**: Mnemonic phrase generation and validation +- **BIP32**: Hierarchical deterministic key derivation +- **ChaCha20**: Stream cipher for NIP-44 encryption +- **AES-CBC**: Block cipher for NIP-04 encryption +- **Schnorr Signatures**: BIP-340 compliant signing and verification + +### Networking & Relay Support +- **Multi-Relay Queries**: Synchronous querying with progress callbacks +- **Relay Pools**: Asynchronous connection management with statistics +- **WebSocket Communication**: Full relay protocol support +- **Event Deduplication**: Automatic handling of duplicate events across relays +- **Connection Management**: Automatic reconnection and error handling + +### Developer Experience +- **Zero Dependencies**: Only requires standard C library and math library (`-lm`) +- **Thread-Safe**: Core cryptographic functions are stateless +- **Cross-Platform**: Builds on Linux, macOS, Windows +- **Comprehensive Examples**: Ready-to-run demonstration programs +- **Automatic Versioning**: Git-tag based version management + +## 📦 Quick Start + +### Installation + +1. **Clone the repository:** + ```bash + git clone https://github.com/yourusername/nostr_core_lib.git + cd nostr_core_lib + ``` + +2. **Build the library:** + ```bash + ./build.sh lib + ``` + +3. **Run examples:** + ```bash + ./build.sh examples + ./examples/simple_keygen + ``` + +### Usage Example + +```c +#include "nostr_core/nostr_core.h" +#include + +int main() { + // Initialize library + if (nostr_init() != NOSTR_SUCCESS) { + fprintf(stderr, "Failed to initialize NOSTR library\n"); + return 1; + } + + // Generate keypair + unsigned char private_key[32], public_key[32]; + nostr_generate_keypair(private_key, public_key); + + // Convert to bech32 format + char nsec[100], npub[100]; + nostr_key_to_bech32(private_key, "nsec", nsec); + nostr_key_to_bech32(public_key, "npub", npub); + + printf("Private key: %s\n", nsec); + printf("Public key: %s\n", npub); + + // Create and sign event + cJSON* event = nostr_create_and_sign_event(1, "Hello NOSTR!", NULL, private_key, 0); + if (event) { + char* json = cJSON_Print(event); + printf("Event: %s\n", json); + free(json); + cJSON_Delete(event); + } + + nostr_cleanup(); + return 0; +} +``` + +**Compile and run:** +```bash +gcc example.c -o example ./libnostr_core.a -lm +./example +``` + +## 🏗️ Building + +### Build Targets + +```bash +./build.sh lib # Build static library (default) +./build.sh examples # Build examples +./build.sh test # Run test suite +./build.sh clean # Clean build artifacts +./build.sh install # Install to system +``` + +### Manual Building + +```bash +# Build static library +make + +# Build examples +make examples + +# Run tests +make test-crypto + +# Clean +make clean +``` + +### Dependencies + +**Required:** +- GCC or compatible C compiler +- Standard C library +- Math library (`-lm`) + +**Included:** +- cJSON (JSON parsing) +- secp256k1 (elliptic curve cryptography) +- mbedTLS components (selected crypto functions) + +## 📚 API Documentation + +### Initialization +```c +int nostr_init(void); // Initialize library (call first) +void nostr_cleanup(void); // Cleanup resources (call last) +const char* nostr_strerror(int error); // Get error message +``` + +### Key Management +```c +// Generate random keypair +int nostr_generate_keypair(unsigned char* private_key, unsigned char* public_key); + +// Generate from mnemonic +int nostr_generate_mnemonic_and_keys(char* mnemonic, size_t mnemonic_size, + int account, unsigned char* private_key, + unsigned char* public_key); + +// Derive from existing mnemonic +int nostr_derive_keys_from_mnemonic(const char* mnemonic, int account, + unsigned char* private_key, unsigned char* public_key); + +// Format conversion +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); +``` + +### Event Creation +```c +// Create and sign event +cJSON* nostr_create_and_sign_event(int kind, const char* content, cJSON* tags, + const unsigned char* private_key, time_t timestamp); + +// Add proof of work +int nostr_add_proof_of_work(cJSON* event, const unsigned char* private_key, + int target_difficulty, void (*progress_callback)(...), void* user_data); +``` + +### Encryption (NIP-04 & NIP-44) +```c +// NIP-04 (AES-CBC) +int nostr_nip04_encrypt(const unsigned char* sender_private_key, + const unsigned char* recipient_public_key, + const char* plaintext, char* output, size_t output_size); + +int nostr_nip04_decrypt(const unsigned char* recipient_private_key, + const unsigned char* sender_public_key, + const char* encrypted_data, char* output, size_t output_size); + +// NIP-44 (ChaCha20) +int nostr_nip44_encrypt(const unsigned char* sender_private_key, + const unsigned char* recipient_public_key, + const char* plaintext, char* output, size_t output_size); + +int nostr_nip44_decrypt(const unsigned char* recipient_private_key, + const unsigned char* sender_public_key, + const char* encrypted_data, char* output, size_t output_size); +``` + +### Relay Communication +```c +// Simple relay query +cJSON* nostr_query_relay_for_event(const char* relay_url, const char* pubkey_hex, int kind); + +// Multi-relay synchronous queries +cJSON** synchronous_query_relays_with_progress(const char** relay_urls, int relay_count, + cJSON* filter, relay_query_mode_t mode, + int* result_count, int relay_timeout_seconds, + relay_progress_callback_t callback, void* user_data); + +// Multi-relay publishing +publish_result_t* synchronous_publish_event_with_progress(const char** relay_urls, int relay_count, + cJSON* event, int* success_count, + int relay_timeout_seconds, + publish_progress_callback_t callback, void* user_data); +``` + +### Relay Pools (Asynchronous) +```c +// Create and manage relay pool +nostr_relay_pool_t* nostr_relay_pool_create(void); +int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url); +void nostr_relay_pool_destroy(nostr_relay_pool_t* pool); + +// Subscribe to events +nostr_pool_subscription_t* nostr_relay_pool_subscribe( + nostr_relay_pool_t* pool, const char** relay_urls, int relay_count, cJSON* filter, + void (*on_event)(cJSON* event, const char* relay_url, void* user_data), + void (*on_eose)(void* user_data), void* user_data); + +// Run event loop +int nostr_relay_pool_run(nostr_relay_pool_t* pool, int timeout_ms); +int nostr_relay_pool_poll(nostr_relay_pool_t* pool, int timeout_ms); +``` + +## 📁 Examples + +The library includes comprehensive examples: + +- **`simple_keygen`** - Basic key generation and formatting +- **`keypair_generation`** - Advanced key management +- **`mnemonic_generation`** - BIP39 mnemonic handling +- **`mnemonic_derivation`** - NIP-06 key derivation +- **`utility_functions`** - General utility demonstrations +- **`input_detection`** - Input type detection and processing +- **`version_test`** - Library version information + +Run all examples: +```bash +./build.sh examples +ls -la examples/ +``` + +## 🧪 Testing + +The library includes extensive tests: + +```bash +# Run all tests +./build.sh test + +# Individual test categories +cd tests && make test +``` + +**Test Categories:** +- **Core Functionality**: `simple_init_test`, `header_test` +- **Cryptography**: `chacha20_test`, `nostr_crypto_test` +- **NIP-04 Encryption**: `nip04_test` +- **NIP-44 Encryption**: `nip44_test`, `nip44_debug_test` +- **Key Derivation**: `nostr_test_bip32` +- **Relay Communication**: `relay_pool_test`, `sync_test` +- **Proof of Work**: `test_pow_loop` + +## 🏗️ Integration + +### Static Library Integration + +1. **Copy required files to your project:** + ```bash + cp libnostr_core.a /path/to/your/project/ + cp nostr_core/nostr_core.h /path/to/your/project/ + ``` + +2. **Link in your project:** + ```bash + gcc your_code.c -L. -lnostr_core -lm -o your_program + ``` + +### Source Integration + +1. **Copy source files:** + ```bash + cp -r nostr_core/ /path/to/your/project/ + cp -r cjson/ /path/to/your/project/ + ``` + +2. **Include in your build:** + ```bash + gcc your_code.c nostr_core/*.c cjson/cJSON.c -lm -o your_program + ``` + +### Self-Contained Library + +The `libnostr_core.a` file is completely self-contained with **no external dependencies**: + +- ✅ **No OpenSSL required** +- ✅ **No libwally required** +- ✅ **No system secp256k1 required** +- ✅ **Only needs math library (`-lm`)** + +```bash +# This is all you need: +gcc your_app.c ./libnostr_core.a -lm -o your_app +``` + +## 🔧 Configuration + +### Compile-Time Options + +```c +// Enable debug output +#define NOSTR_DEBUG_ENABLED + +// Crypto-only build (no networking) +#define NOSTR_CRYPTO_ONLY + +// Enable specific NIPs +#define NOSTR_NIP04_ENABLED +#define NOSTR_NIP44_ENABLED +#define NOSTR_NIP13_ENABLED +``` + +### Build Flags + +```bash +# Enable all logging +make LOGGING_FLAGS="-DENABLE_FILE_LOGGING -DENABLE_WEBSOCKET_LOGGING -DENABLE_DEBUG_LOGGING" + +# Debug build +make debug + +# ARM64 cross-compile +make arm64 +``` + +## 🌐 Supported Platforms + +- **Linux** (x86_64, ARM64) +- **macOS** (Intel, Apple Silicon) +- **Windows** (MinGW, MSYS2) +- **Embedded Systems** (resource-constrained environments) + +## 📄 Documentation + +- **[LIBRARY_USAGE.md](LIBRARY_USAGE.md)** - Detailed integration guide +- **[EXPORT_GUIDE.md](EXPORT_GUIDE.md)** - Library export instructions +- **[AUTOMATIC_VERSIONING.md](AUTOMATIC_VERSIONING.md)** - Version management +- **API Reference** - Complete documentation in `nostr_core/nostr_core.h` + +## 🤝 Contributing + +1. Fork the repository +2. Create a feature branch: `git checkout -b feature/amazing-feature` +3. Make your changes and add tests +4. Run the test suite: `./build.sh test` +5. Commit your changes: `git commit -m 'Add amazing feature'` +6. Push to the branch: `git push origin feature/amazing-feature` +7. Open a Pull Request + +## 📈 Version History + +Current version: **0.1.8** + +The library uses automatic semantic versioning based on Git tags. Each build increments the patch version automatically. + +- `v0.1.x` - Initial development releases +- Focus on core protocol implementation and self-contained crypto +- Full NIP-01, NIP-04, NIP-06, NIP-13, NIP-44 support + +## 🐛 Troubleshooting + +### Common Issues + +**Build fails with secp256k1 errors:** +```bash +cd secp256k1 +./autogen.sh +./configure --enable-module-schnorrsig --enable-module-ecdh +make +cd .. +./build.sh lib +``` + +**Library too large:** +The library is intentionally large (~2.4MB) because it includes all secp256k1 cryptographic functions for complete self-containment. + +**Linking errors:** +Make sure to include the math library: +```bash +gcc your_code.c ./libnostr_core.a -lm # Note the -lm flag +``` + +### Getting Help + +- Check the `examples/` directory for working code +- Run `./build.sh test` to verify your environment +- Review the comprehensive API documentation in `nostr_core/nostr_core.h` + +## 📜 License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## 🙏 Acknowledgments + +- **NOSTR Protocol** - The decentralized social media protocol +- **secp256k1** - Bitcoin's elliptic curve library +- **cJSON** - Lightweight JSON parser +- **mbedTLS** - Cryptographic building blocks +- **NOSTR Community** - For protocol specification and feedback + +--- + +**Built with ❤️ for the decentralized web** + +*Self-contained • Zero dependencies • Production ready* diff --git a/VERSION b/VERSION index d917d3e2..04c5555c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.2 +0.1.17 diff --git a/build.sh b/build.sh index 62b95d71..f005d25e 100755 --- a/build.sh +++ b/build.sh @@ -146,23 +146,27 @@ show_usage() { echo "NOSTR Core Library Build Script" echo "===============================" echo "" - echo "Usage: $0 [target]" + echo "Usage: $0 [target] [architecture]" echo "" echo "Available targets:" echo " clean - Clean all build artifacts" - echo " lib - Build static library (default)" - echo " shared - Build shared library" - echo " all - Build both static and shared libraries" + echo " lib - Build static libraries for both x64 and ARM64 (default)" + echo " x64 - Build x64 static library only" + echo " arm64 - Build ARM64 static library only" + echo " all - Build both architectures and examples" echo " examples - Build example programs" echo " test - Run tests" echo " install - Install library to system" echo " uninstall - Remove library from system" echo " help - Show this help message" echo "" - echo "Library outputs:" - echo " libnostr_core.a - Static library" - echo " libnostr_core.so - Shared library" + echo "Library outputs (both self-contained with secp256k1):" + echo " libnostr_core.a - x86_64 static library" + echo " libnostr_core_arm64.a - ARM64 static library" echo " examples/* - Example programs" + echo "" + echo "Both libraries include secp256k1 objects internally." + echo "Users only need to link with the library + -lm." } # Parse command line arguments @@ -177,15 +181,63 @@ case "$TARGET" in lib|library) increment_version - print_status "Building static library..." + print_status "Building both x64 and ARM64 static libraries..." make clean make + + # Check both libraries were built + SUCCESS=0 + if [ -f "libnostr_core.a" ]; then + SIZE_X64=$(stat -c%s "libnostr_core.a") + print_success "x64 static library built successfully (${SIZE_X64} bytes)" + SUCCESS=$((SUCCESS + 1)) + else + print_error "Failed to build x64 static library" + fi + + if [ -f "libnostr_core_arm64.a" ]; then + SIZE_ARM64=$(stat -c%s "libnostr_core_arm64.a") + print_success "ARM64 static library built successfully (${SIZE_ARM64} bytes)" + SUCCESS=$((SUCCESS + 1)) + else + print_error "Failed to build ARM64 static library" + fi + + if [ $SUCCESS -eq 2 ]; then + print_success "Both architectures built successfully!" + ls -la libnostr_core*.a + else + print_error "Failed to build all libraries" + exit 1 + fi + ;; + + x64|x64-only) + increment_version + print_status "Building x64 static library only..." + make clean + make x64 if [ -f "libnostr_core.a" ]; then SIZE=$(stat -c%s "libnostr_core.a") - print_success "Static library built successfully (${SIZE} bytes)" + print_success "x64 static library built successfully (${SIZE} bytes)" ls -la libnostr_core.a else - print_error "Failed to build static library" + print_error "Failed to build x64 static library" + exit 1 + fi + ;; + + arm64|arm64-only) + increment_version + print_status "Building ARM64 static library only..." + make clean + make arm64 + if [ -f "libnostr_core_arm64.a" ]; then + SIZE=$(stat -c%s "libnostr_core_arm64.a") + print_success "ARM64 static library built successfully (${SIZE} bytes)" + ls -la libnostr_core_arm64.a + else + print_error "Failed to build ARM64 static library" exit 1 fi ;; @@ -207,21 +259,54 @@ case "$TARGET" in all) increment_version - print_status "Building all libraries..." + print_status "Building all libraries and examples..." make clean make all - print_success "All libraries built successfully" - ls -la libnostr_core.* + + # Check both libraries and examples were built + SUCCESS=0 + if [ -f "libnostr_core.a" ]; then + SIZE_X64=$(stat -c%s "libnostr_core.a") + print_success "x64 static library built successfully (${SIZE_X64} bytes)" + SUCCESS=$((SUCCESS + 1)) + else + print_error "Failed to build x64 static library" + fi + + if [ -f "libnostr_core_arm64.a" ]; then + SIZE_ARM64=$(stat -c%s "libnostr_core_arm64.a") + print_success "ARM64 static library built successfully (${SIZE_ARM64} bytes)" + SUCCESS=$((SUCCESS + 1)) + else + print_error "Failed to build ARM64 static library" + fi + + if [ $SUCCESS -eq 2 ]; then + print_success "All libraries and examples built successfully!" + ls -la libnostr_core*.a + ls -la examples/ + else + print_error "Failed to build all components" + exit 1 + fi ;; examples) increment_version - print_status "Building examples..." + print_status "Building both libraries and examples..." make clean make make examples - print_success "Examples built successfully" - ls -la examples/ + + # Verify libraries were built + if [ -f "libnostr_core.a" ] && [ -f "libnostr_core_arm64.a" ]; then + print_success "Both libraries and examples built successfully" + ls -la libnostr_core*.a + ls -la examples/ + else + print_error "Failed to build libraries for examples" + exit 1 + fi ;; test) diff --git a/nostr_core/core.o b/nostr_core/core.o index 7e4905607177332f5d9e07f64152d33f5707a703..b9cdae4bdcc5659c6e3a8f4355f229912ab09293 100644 GIT binary patch literal 18896 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 W+mDWk!}UMzvwA$FI?89Z{r?LQhQp%( literal 18344 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 LhVm_klSTgro#YNo diff --git a/nostr_core/nostr_crypto.c b/nostr_core/nostr_crypto.c index 230995b7..791ce126 100644 --- a/nostr_core/nostr_crypto.c +++ b/nostr_core/nostr_crypto.c @@ -24,10 +24,11 @@ // UTILITY FUNCTIONS // ============================================================================= -// Memory clearing utility -static void memory_clear(void *p, size_t len) { +// Memory clearing utility - accepts const pointers for security clearing +static void memory_clear(const void *p, size_t len) { if (p && len) { - memset(p, 0, len); + // Cast away const for memset - this is safe for security clearing + memset((void *)p, 0, len); } } diff --git a/tests/Makefile b/tests/Makefile index abbb8921..fc11c92d 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -16,6 +16,7 @@ RELAY_POOL_TEST_EXEC = relay_pool_test EVENT_GEN_TEST_EXEC = test_event_generation POW_LOOP_TEST_EXEC = test_pow_loop NIP04_TEST_EXEC = nip04_test +STATIC_LINKING_TEST_EXEC = static_linking_only_test ARM64_CRYPTO_TEST_EXEC = nostr_crypto_test_arm64 ARM64_CORE_TEST_EXEC = nostr_core_test_arm64 ARM64_RELAY_POOL_TEST_EXEC = relay_pool_test_arm64 @@ -54,6 +55,11 @@ $(NIP04_TEST_EXEC): nip04_test.c @echo "Building NIP-04 encryption test suite (x86_64)..." $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) +# Build static linking test executable (x86_64) +$(STATIC_LINKING_TEST_EXEC): static_linking_only_test.c + @echo "Building static linking verification test (x86_64)..." + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + # Build simple initialization test executable (x86_64) simple_init_test: simple_init_test.c @echo "Building simple initialization test program (x86_64)..." @@ -142,8 +148,13 @@ test-nip04: $(NIP04_TEST_EXEC) @echo "Running NIP-04 encryption tests (x86_64)..." ./$(NIP04_TEST_EXEC) +# Run static linking verification test (x86_64) +test-static-linking: $(STATIC_LINKING_TEST_EXEC) + @echo "Running static linking verification test (x86_64)..." + ./$(STATIC_LINKING_TEST_EXEC) + # Run all test suites (x86_64) -test: test-crypto test-core test-relay-pool test-nip04 +test: test-crypto test-core test-relay-pool test-nip04 test-static-linking # Run crypto tests ARM64 (requires qemu-user-static or ARM64 system) test-crypto-arm64: $(ARM64_CRYPTO_TEST_EXEC) @@ -190,7 +201,7 @@ test-all: test test-arm64 # Clean clean: @echo "Cleaning test artifacts..." - rm -f $(CRYPTO_TEST_EXEC) $(CORE_TEST_EXEC) $(RELAY_POOL_TEST_EXEC) $(EVENT_GEN_TEST_EXEC) $(POW_LOOP_TEST_EXEC) $(NIP04_TEST_EXEC) $(ARM64_CRYPTO_TEST_EXEC) $(ARM64_CORE_TEST_EXEC) $(ARM64_RELAY_POOL_TEST_EXEC) $(ARM64_NIP04_TEST_EXEC) + rm -f $(CRYPTO_TEST_EXEC) $(CORE_TEST_EXEC) $(RELAY_POOL_TEST_EXEC) $(EVENT_GEN_TEST_EXEC) $(POW_LOOP_TEST_EXEC) $(NIP04_TEST_EXEC) $(STATIC_LINKING_TEST_EXEC) $(ARM64_CRYPTO_TEST_EXEC) $(ARM64_CORE_TEST_EXEC) $(ARM64_RELAY_POOL_TEST_EXEC) $(ARM64_NIP04_TEST_EXEC) # Help help: @@ -198,17 +209,18 @@ help: @echo "================" @echo "" @echo "Available targets:" - @echo " all - Build all test executables (x86_64)" - @echo " all-arch - Build test executables for both x86_64 and ARM64" - @echo " test-crypto - Build and run crypto tests (x86_64)" - @echo " test-core - Build and run core tests (x86_64)" - @echo " test-relay-pool - Build and run relay pool tests (x86_64)" - @echo " test-nip04 - Build and run NIP-04 encryption tests (x86_64)" - @echo " test - Build and run all test suites (x86_64)" - @echo " test-arm64 - Build and run all test suites (ARM64)" - @echo " test-all - Run tests on both architectures" - @echo " clean - Remove test artifacts" - @echo " help - Show this help" + @echo " all - Build all test executables (x86_64)" + @echo " all-arch - Build test executables for both x86_64 and ARM64" + @echo " test-crypto - Build and run crypto tests (x86_64)" + @echo " test-core - Build and run core tests (x86_64)" + @echo " test-relay-pool - Build and run relay pool tests (x86_64)" + @echo " test-nip04 - Build and run NIP-04 encryption tests (x86_64)" + @echo " test-static-linking - Build and run static linking verification test (x86_64)" + @echo " test - Build and run all test suites (x86_64)" + @echo " test-arm64 - Build and run all test suites (ARM64)" + @echo " test-all - Run tests on both architectures" + @echo " clean - Remove test artifacts" + @echo " help - Show this help" @echo "" @echo "Test Executables:" @echo " $(CRYPTO_TEST_EXEC) - x86_64 crypto test binary" @@ -221,9 +233,10 @@ help: @echo " $(ARM64_NIP04_TEST_EXEC) - ARM64 NIP-04 encryption test binary" @echo "" @echo "Test Coverage:" - @echo " Crypto Tests - Low-level cryptographic primitives (SHA-256, HMAC, secp256k1, BIP39, BIP32)" - @echo " Core Tests - High-level NOSTR functionality with nak compatibility validation" - @echo " Relay Pool Tests - Relay pool event processing with real NOSTR relays" - @echo " NIP-04 Tests - NOSTR NIP-04 encryption/decryption with reference test vectors" + @echo " Crypto Tests - Low-level cryptographic primitives (SHA-256, HMAC, secp256k1, BIP39, BIP32)" + @echo " Core Tests - High-level NOSTR functionality with nak compatibility validation" + @echo " Relay Pool Tests - Relay pool event processing with real NOSTR relays" + @echo " NIP-04 Tests - NOSTR NIP-04 encryption/decryption with reference test vectors" + @echo " Static Linking Tests - Verify library has no external crypto dependencies (self-contained)" -.PHONY: all all-arch test-crypto test-core test-relay-pool test test-crypto-arm64 test-core-arm64 test-relay-pool-arm64 test-arm64 test-all clean help +.PHONY: all all-arch test-crypto test-core test-relay-pool test-nip04 test-static-linking test test-crypto-arm64 test-core-arm64 test-relay-pool-arm64 test-arm64 test-all clean help diff --git a/tests/static_linking_only_test.c b/tests/static_linking_only_test.c new file mode 100644 index 00000000..f6d97eec --- /dev/null +++ b/tests/static_linking_only_test.c @@ -0,0 +1,416 @@ +/* + * NOSTR Core Library - Static Linking Only Test + * + * This test verifies that the library maintains its self-contained, + * static-only design with no external cryptographic dependencies. + * + * Test Categories: + * 1. Library dependency analysis using ldd/otool + * 2. Symbol resolution verification using nm/objdump + * 3. Build process validation + * 4. Runtime independence verification + * 5. Library size and content verification + */ + +#define _GNU_SOURCE // For popen/pclose on Linux +#include "../nostr_core/nostr_core.h" +#include +#include +#include +#include +#include +#include +#include "../cjson/cJSON.h" + +// ANSI color codes for output +#define GREEN "\033[32m" +#define RED "\033[31m" +#define YELLOW "\033[33m" +#define BLUE "\033[34m" +#define RESET "\033[0m" + +// Test result tracking +static int tests_run = 0; +static int tests_passed = 0; + +// Helper function to run shell commands and capture output +static int run_command(const char* command, char* output, size_t output_size) { + FILE* fp = popen(command, "r"); + if (!fp) { + return -1; + } + + size_t total = 0; + while (total < output_size - 1 && fgets(output + total, output_size - total, fp)) { + total = strlen(output); + } + + int status = pclose(fp); + return WEXITSTATUS(status); +} + +// Helper function to check if file exists +static int file_exists(const char* path) { + struct stat st; + return stat(path, &st) == 0; +} + +// Test macro +#define RUN_TEST(test_name, test_func) do { \ + printf(BLUE "[TEST] " RESET "%s...\n", test_name); \ + tests_run++; \ + if (test_func()) { \ + printf(GREEN "[PASS] " RESET "%s\n\n", test_name); \ + tests_passed++; \ + } else { \ + printf(RED "[FAIL] " RESET "%s\n\n", test_name); \ + } \ +} while(0) + +// Test 1: Library Dependency Analysis +static int test_library_dependency_analysis(void) { + char command[512]; + char output[4096]; + int result; + + // Check if we have the main library + if (!file_exists("../libnostr_core.a")) { + printf(RED "ERROR: " RESET "libnostr_core.a not found. Run 'make' first.\n"); + return 0; + } + + // Create a simple test binary to analyze + printf("Creating test binary for dependency analysis...\n"); + + const char* test_code = + "#include \"../nostr_core/nostr_core.h\"\n" + "#include \n" + "int main() {\n" + " if (nostr_init() == NOSTR_SUCCESS) {\n" + " unsigned char privkey[32], pubkey[32];\n" + " if (nostr_generate_keypair(privkey, pubkey) == NOSTR_SUCCESS) {\n" + " printf(\"Crypto test passed\\n\");\n" + " }\n" + " nostr_cleanup();\n" + " }\n" + " return 0;\n" + "}\n"; + + FILE* fp = fopen("/tmp/static_test.c", "w"); + if (!fp) { + printf(RED "ERROR: " RESET "Cannot create temporary test file\n"); + return 0; + } + fputs(test_code, fp); + fclose(fp); + + // Compile the test binary + snprintf(command, sizeof(command), + "gcc -I.. -Wall -Wextra -std=c99 /tmp/static_test.c -o /tmp/static_test ../libnostr_core.a -lm -static 2>/dev/null"); + + result = system(command); + if (result != 0) { + printf(RED "ERROR: " RESET "Failed to compile test binary\n"); + return 0; + } + + // Analyze dependencies with ldd (Linux) or otool (macOS) + printf("Analyzing binary dependencies...\n"); + +#ifdef __linux__ + snprintf(command, sizeof(command), "ldd /tmp/static_test 2>&1"); +#elif __APPLE__ + snprintf(command, sizeof(command), "otool -L /tmp/static_test 2>&1"); +#else + printf(YELLOW "WARNING: " RESET "Unknown platform, skipping dependency analysis\n"); + cleanup_and_return: + unlink("/tmp/static_test.c"); + unlink("/tmp/static_test"); + return 1; +#endif + + result = run_command(command, output, sizeof(output)); + + // Check for problematic dynamic dependencies + const char* forbidden_libs[] = { + "libsecp256k1", + "libssl", + "libcrypto", + "libwally", + "libsodium" + }; + + int found_forbidden = 0; + for (int i = 0; i < 5; i++) { + if (strstr(output, forbidden_libs[i])) { + printf(RED "ERROR: " RESET "Found forbidden dynamic dependency: %s\n", forbidden_libs[i]); + found_forbidden = 1; + } + } + + if (!found_forbidden) { + printf(GREEN "GOOD: " RESET "No forbidden cryptographic dependencies found\n"); + } + + // For static binaries, ldd should say "not a dynamic executable" or show minimal deps +#ifdef __linux__ + if (strstr(output, "not a dynamic executable") || strstr(output, "statically linked")) { + printf(GREEN "EXCELLENT: " RESET "Binary is statically linked\n"); + } else { + printf(YELLOW "INFO: " RESET "Binary appears to have some dynamic dependencies:\n"); + printf("%s\n", output); + } +#endif + + // Cleanup + unlink("/tmp/static_test.c"); + unlink("/tmp/static_test"); + + return !found_forbidden; +} + +// Test 2: Symbol Resolution Verification +static int test_symbol_resolution_verification(void) { + char command[512]; + char output[8192]; + + printf("Verifying secp256k1 symbols are present in static library...\n"); + + // Check that critical secp256k1 symbols are present + snprintf(command, sizeof(command), "nm ../libnostr_core.a 2>/dev/null | grep secp256k1"); + + if (run_command(command, output, sizeof(output)) != 0 || strlen(output) == 0) { + printf(RED "ERROR: " RESET "No secp256k1 symbols found in library\n"); + return 0; + } + + // Check for key secp256k1 functions + const char* required_symbols[] = { + "secp256k1_context_create", + "secp256k1_ec_pubkey_create", + "secp256k1_schnorrsig_sign", + "secp256k1_schnorrsig_verify", + "secp256k1_ecdh" + }; + + int symbols_found = 0; + for (int i = 0; i < 5; i++) { + if (strstr(output, required_symbols[i])) { + symbols_found++; + printf(GREEN "FOUND: " RESET "%s\n", required_symbols[i]); + } else { + printf(YELLOW "MISSING: " RESET "%s\n", required_symbols[i]); + } + } + + if (symbols_found >= 3) { + printf(GREEN "GOOD: " RESET "Found %d/5 critical secp256k1 symbols\n", symbols_found); + return 1; + } else { + printf(RED "ERROR: " RESET "Only found %d/5 critical secp256k1 symbols\n", symbols_found); + return 0; + } +} + +// Test 3: Build Process Validation +static int test_build_process_validation(void) { + char command[512]; + int result; + + printf("Testing minimal build requirements...\n"); + + // Test that we can build with only libnostr_core.a and -lm + const char* minimal_test = + "#include \"../nostr_core/nostr_core.h\"\n" + "int main() { return nostr_init() == NOSTR_SUCCESS ? 0 : 1; }\n"; + + FILE* fp = fopen("/tmp/minimal_test.c", "w"); + if (!fp) return 0; + fputs(minimal_test, fp); + fclose(fp); + + // Try to build with minimal dependencies + snprintf(command, sizeof(command), + "gcc -I.. -Wall -Wextra -std=c99 /tmp/minimal_test.c -o /tmp/minimal_test ../libnostr_core.a -lm 2>/dev/null"); + + result = system(command); + + unlink("/tmp/minimal_test.c"); + + if (result == 0) { + printf(GREEN "EXCELLENT: " RESET "Can build with only libnostr_core.a and -lm\n"); + + // Test that it actually runs + result = system("/tmp/minimal_test"); + unlink("/tmp/minimal_test"); + + if (result == 0) { + printf(GREEN "EXCELLENT: " RESET "Minimal binary runs successfully\n"); + return 1; + } else { + printf(RED "ERROR: " RESET "Minimal binary failed to run\n"); + return 0; + } + } else { + printf(RED "ERROR: " RESET "Cannot build with minimal dependencies\n"); + unlink("/tmp/minimal_test"); + return 0; + } +} + +// Test 4: Runtime Independence Test +static int test_runtime_independence(void) { + printf("Testing runtime independence (crypto functionality)...\n"); + + // Initialize the library + if (nostr_init() != NOSTR_SUCCESS) { + printf(RED "ERROR: " RESET "Library initialization failed\n"); + return 0; + } + + // Test key generation + unsigned char private_key[32]; + unsigned char public_key[32]; + + if (nostr_generate_keypair(private_key, public_key) != NOSTR_SUCCESS) { + printf(RED "ERROR: " RESET "Key generation failed\n"); + nostr_cleanup(); + return 0; + } + printf(GREEN "GOOD: " RESET "Key generation works\n"); + + // Test bech32 encoding + char nsec[100], npub[100]; + if (nostr_key_to_bech32(private_key, "nsec", nsec) != NOSTR_SUCCESS || + nostr_key_to_bech32(public_key, "npub", npub) != NOSTR_SUCCESS) { + printf(RED "ERROR: " RESET "Bech32 encoding failed\n"); + nostr_cleanup(); + return 0; + } + printf(GREEN "GOOD: " RESET "Bech32 encoding works\n"); + + // Test signing + cJSON* event = nostr_create_and_sign_event(1, "Test message", NULL, private_key, 0); + if (!event) { + printf(RED "ERROR: " RESET "Event creation/signing failed\n"); + nostr_cleanup(); + return 0; + } + printf(GREEN "GOOD: " RESET "Event signing works\n"); + cJSON_Delete(event); + + // Test NIP-44 encryption if available + char plaintext[] = "Hello, NOSTR!"; + char encrypted[1024]; + char decrypted[1024]; + + // Generate recipient keys + unsigned char recipient_private[32], recipient_public[32]; + nostr_generate_keypair(recipient_private, recipient_public); + + if (nostr_nip44_encrypt(private_key, recipient_public, plaintext, encrypted, sizeof(encrypted)) == NOSTR_SUCCESS) { + if (nostr_nip44_decrypt(recipient_private, public_key, encrypted, decrypted, sizeof(decrypted)) == NOSTR_SUCCESS) { + if (strcmp(plaintext, decrypted) == 0) { + printf(GREEN "EXCELLENT: " RESET "NIP-44 encryption/decryption works\n"); + } else { + printf(YELLOW "WARNING: " RESET "NIP-44 decryption mismatch\n"); + } + } else { + printf(YELLOW "WARNING: " RESET "NIP-44 decryption failed\n"); + } + } else { + printf(YELLOW "WARNING: " RESET "NIP-44 encryption failed (may not be enabled)\n"); + } + + nostr_cleanup(); + return 1; +} + +// Test 5: Library Size and Content Verification +static int test_library_size_and_content(void) { + struct stat st; + char command[512]; + char output[4096]; + + printf("Verifying library size and content...\n"); + + // Check library size + if (stat("../libnostr_core.a", &st) != 0) { + printf(RED "ERROR: " RESET "Cannot stat libnostr_core.a\n"); + return 0; + } + + size_t lib_size = st.st_size; + printf("Library size: %zu bytes (%.2f MB)\n", lib_size, lib_size / 1024.0 / 1024.0); + + // Expect "fat" library to be at least 1MB (with secp256k1 bundled) + if (lib_size < 1024 * 1024) { + printf(YELLOW "WARNING: " RESET "Library seems small (%.2f MB). May not include secp256k1.\n", + lib_size / 1024.0 / 1024.0); + } else { + printf(GREEN "GOOD: " RESET "Library size suggests secp256k1 is bundled\n"); + } + + // List archive contents + snprintf(command, sizeof(command), "ar -t ../libnostr_core.a | wc -l"); + if (run_command(command, output, sizeof(output)) == 0) { + int object_count = atoi(output); + printf("Archive contains %d object files\n", object_count); + + if (object_count > 20) { + printf(GREEN "EXCELLENT: " RESET "High object count suggests secp256k1 objects included\n"); + } else { + printf(YELLOW "WARNING: " RESET "Low object count (%d). secp256k1 may not be fully bundled\n", object_count); + } + } + + // Check for secp256k1-specific object files + snprintf(command, sizeof(command), "ar -t ../libnostr_core.a | grep -E '(secp256k1|ecmult)' | head -5"); + if (run_command(command, output, sizeof(output)) == 0 && strlen(output) > 0) { + printf(GREEN "EXCELLENT: " RESET "Found secp256k1 object files in archive:\n"); + printf("%s", output); + } else { + printf(YELLOW "WARNING: " RESET "No obvious secp256k1 object files found\n"); + } + + return 1; +} + +// Main test runner +int main(int argc, char* argv[]) { + (void)argc; + (void)argv; + + printf(BLUE "NOSTR Core Library - Static Linking Only Test\n"); + printf("==============================================" RESET "\n\n"); + + printf("This test verifies that the library maintains its self-contained,\n"); + printf("static-only design with no external cryptographic dependencies.\n\n"); + + // Run all tests + RUN_TEST("Library Dependency Analysis", test_library_dependency_analysis); + RUN_TEST("Symbol Resolution Verification", test_symbol_resolution_verification); + RUN_TEST("Build Process Validation", test_build_process_validation); + RUN_TEST("Runtime Independence Test", test_runtime_independence); + RUN_TEST("Library Size and Content Verification", test_library_size_and_content); + + // Print summary + printf(BLUE "============================================\n"); + printf("TEST SUMMARY\n"); + printf("============================================" RESET "\n"); + printf("Tests run: %d\n", tests_run); + printf("Tests passed: %d\n", tests_passed); + + if (tests_passed == tests_run) { + printf(GREEN "ALL TESTS PASSED!" RESET "\n"); + printf("✅ Library maintains static-only design\n"); + printf("✅ No external crypto dependencies\n"); + printf("✅ Self-contained and portable\n"); + } else { + printf(RED "SOME TESTS FAILED!" RESET "\n"); + printf("❌ %d out of %d tests failed\n", tests_run - tests_passed, tests_run); + printf("⚠️ Library may have external dependencies or missing components\n"); + } + + return (tests_passed == tests_run) ? 0 : 1; +}