Completed refactoring to separate nip files, and updating build.sh

This commit is contained in:
Laan Tungir 2025-08-16 07:42:48 -04:00
parent 8ed9262c65
commit c3a9482882
37 changed files with 2693 additions and 3578 deletions

View File

@ -2,8 +2,6 @@ This library is fully staticly linked. There should be no external dependencies.
When building, use build.sh, not make.
Use it as follows: build.sh -m "useful comment on changes being made"
When making TUI menus, try to use the first leter of the command and the key to press to execute that command. For example, if the command is "Open file" try to use a keypress of "o" upper or lower case to signal to open the file. Use this instead of number keyed menus when possible. In the command, the letter should be underlined that signifies the command.
When deleting, everything gets moved to the Trash folder.

View File

@ -1,187 +0,0 @@
cmake_minimum_required(VERSION 3.12)
project(nostr_core VERSION 1.0.0 LANGUAGES C)
# Set C standard
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
# Build options
option(NOSTR_BUILD_STATIC "Build static library" ON)
option(NOSTR_BUILD_SHARED "Build shared library" ON)
option(NOSTR_BUILD_EXAMPLES "Build examples" ON)
option(NOSTR_BUILD_TESTS "Build tests" ON)
option(NOSTR_ENABLE_WEBSOCKETS "Enable WebSocket support" ON)
option(NOSTR_USE_MBEDTLS "Use mbedTLS for crypto (otherwise use built-in)" OFF)
# Compiler flags
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror")
set(CMAKE_C_FLAGS_DEBUG "-g -O0")
set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
endif()
# Include directories
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/cjson)
# Source files
set(NOSTR_CORE_SOURCES
nostr_core/core.c
nostr_core/core_relays.c
nostr_core/core_relay_pool.c
nostr_core/nostr_crypto.c
nostr_core/nostr_secp256k1.c
cjson/cJSON.c
)
set(NOSTR_CORE_HEADERS
nostr_core.h
nostr_crypto.h
cjson/cJSON.h
)
# Add mbedTLS if enabled
if(NOSTR_USE_MBEDTLS)
add_subdirectory(mbedtls)
list(APPEND NOSTR_CORE_SOURCES mbedtls_wrapper.c)
add_definitions(-DNOSTR_USE_MBEDTLS=1)
endif()
# Add WebSocket support if enabled
if(NOSTR_ENABLE_WEBSOCKETS)
file(GLOB WEBSOCKET_SOURCES "nostr_websocket/*.c")
file(GLOB WEBSOCKET_HEADERS "nostr_websocket/*.h")
list(APPEND NOSTR_CORE_SOURCES ${WEBSOCKET_SOURCES})
list(APPEND NOSTR_CORE_HEADERS ${WEBSOCKET_HEADERS})
add_definitions(-DNOSTR_ENABLE_WEBSOCKETS=1)
endif()
# Create static library
if(NOSTR_BUILD_STATIC)
add_library(nostr_core_static STATIC ${NOSTR_CORE_SOURCES})
set_target_properties(nostr_core_static PROPERTIES OUTPUT_NAME nostr_core)
target_link_libraries(nostr_core_static m)
if(NOSTR_USE_MBEDTLS)
target_link_libraries(nostr_core_static mbedcrypto mbedx509 mbedtls)
endif()
endif()
# Create shared library
if(NOSTR_BUILD_SHARED)
add_library(nostr_core_shared SHARED ${NOSTR_CORE_SOURCES})
set_target_properties(nostr_core_shared PROPERTIES OUTPUT_NAME nostr_core)
target_link_libraries(nostr_core_shared m)
if(NOSTR_USE_MBEDTLS)
target_link_libraries(nostr_core_shared mbedcrypto mbedx509 mbedtls)
endif()
# Set version information
set_target_properties(nostr_core_shared PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 1
)
endif()
# Create alias targets for easier integration
if(NOSTR_BUILD_STATIC)
add_library(nostr_core::static ALIAS nostr_core_static)
endif()
if(NOSTR_BUILD_SHARED)
add_library(nostr_core::shared ALIAS nostr_core_shared)
endif()
# Examples
if(NOSTR_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
# Tests
if(NOSTR_BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# Installation
include(GNUInstallDirs)
# Install libraries
if(NOSTR_BUILD_STATIC)
install(TARGETS nostr_core_static
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endif()
if(NOSTR_BUILD_SHARED)
install(TARGETS nostr_core_shared
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
# Install headers
install(FILES ${NOSTR_CORE_HEADERS}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/nostr
)
# Install pkg-config file
configure_file(nostr_core.pc.in nostr_core.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/nostr_core.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)
# Generate export configuration
include(CMakePackageConfigHelpers)
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/nostr_core-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/nostr_core-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/nostr_core
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/nostr_core-config-version.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/nostr_core-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/nostr_core-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/nostr_core
)
# Export targets
if(NOSTR_BUILD_STATIC OR NOSTR_BUILD_SHARED)
set(EXPORT_TARGETS "")
if(NOSTR_BUILD_STATIC)
list(APPEND EXPORT_TARGETS nostr_core_static)
endif()
if(NOSTR_BUILD_SHARED)
list(APPEND EXPORT_TARGETS nostr_core_shared)
endif()
install(EXPORT nostr_core-targets
FILE nostr_core-targets.cmake
NAMESPACE nostr_core::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/nostr_core
)
export(TARGETS ${EXPORT_TARGETS}
FILE ${CMAKE_CURRENT_BINARY_DIR}/nostr_core-targets.cmake
NAMESPACE nostr_core::
)
endif()
# Summary
message(STATUS "NOSTR Core Library Configuration:")
message(STATUS " Version: ${PROJECT_VERSION}")
message(STATUS " Build static library: ${NOSTR_BUILD_STATIC}")
message(STATUS " Build shared library: ${NOSTR_BUILD_SHARED}")
message(STATUS " Build examples: ${NOSTR_BUILD_EXAMPLES}")
message(STATUS " Build tests: ${NOSTR_BUILD_TESTS}")
message(STATUS " Enable WebSockets: ${NOSTR_ENABLE_WEBSOCKETS}")
message(STATUS " Use mbedTLS: ${NOSTR_USE_MBEDTLS}")
message(STATUS " Install prefix: ${CMAKE_INSTALL_PREFIX}")

495
Makefile
View File

@ -1,495 +0,0 @@
# NOSTR Core Library Makefile
# Standalone library build system
CC = gcc
AR = ar
CFLAGS = -Wall -Wextra -std=c99 -fPIC -O2
DEBUG_CFLAGS = -Wall -Wextra -std=c99 -fPIC -g -DDEBUG
STATIC_CFLAGS = -Wall -Wextra -std=c99 -O2 -static
# Logging compile flags
LOGGING_FLAGS ?= -DENABLE_FILE_LOGGING -DENABLE_WEBSOCKET_LOGGING -DENABLE_DEBUG_LOGGING
ifneq ($(ENABLE_LOGGING),)
LOGGING_FLAGS += -DENABLE_FILE_LOGGING -DENABLE_WEBSOCKET_LOGGING -DENABLE_DEBUG_LOGGING
endif
# Include paths
INCLUDES = -I. -Inostr_core -Inostr_core/crypto -Icjson -Isecp256k1/include -Inostr_websocket -I./openssl-install/include
# Library source files - modular NIP-based structure
LIB_SOURCES = nostr_core/crypto/nostr_crypto.c nostr_core/crypto/nostr_secp256k1.c nostr_core/crypto/nostr_aes.c nostr_core/crypto/nostr_chacha20.c cjson/cJSON.c nostr_core/utils.c nostr_core/nip001.c nostr_core/nip005.c nostr_core/nip006.c nostr_core/nip011.c nostr_core/nip013.c nostr_core/nip019.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
# OpenSSL library paths
OPENSSL_LIB_SSL = ./openssl-install/lib64/libssl.a
OPENSSL_LIB_CRYPTO = ./openssl-install/lib64/libcrypto.a
# curl library paths
CURL_LIB = ./curl-install/lib/libcurl.a
# Library outputs (static only)
STATIC_LIB = libnostr_core.a
ARM64_STATIC_LIB = libnostr_core_arm64.a
# Example files
EXAMPLE_SOURCES = $(wildcard examples/*.c)
EXAMPLE_TARGETS = $(EXAMPLE_SOURCES:.c=)
# Default target - build both x64 and ARM64 static libraries
default: $(STATIC_LIB) $(ARM64_STATIC_LIB)
# Build all targets (static only)
all: $(STATIC_LIB) $(ARM64_STATIC_LIB) examples
# Build secp256k1 for x86_64
$(SECP256K1_LIB): secp256k1/configure
@echo "Building secp256k1 for x86_64..."
@cd secp256k1 && \
if [ ! -f .libs/libsecp256k1.a ]; then \
echo "Cleaning and configuring secp256k1..."; \
make distclean >/dev/null 2>&1 || true; \
./configure --enable-module-schnorrsig --enable-module-ecdh --enable-experimental --disable-shared --enable-static --with-pic; \
echo "Building secp256k1 library..."; \
make -j$(shell nproc 2>/dev/null || echo 4); \
else \
echo "secp256k1 library already exists, skipping build"; \
fi
@echo "x86_64 secp256k1 library built successfully"
# Build OpenSSL for x86_64 (minimal build optimized for curl)
$(OPENSSL_LIB_SSL) $(OPENSSL_LIB_CRYPTO): openssl-3.4.2/Configure
@echo "Building minimal OpenSSL for x86_64 (optimized for curl)..."
@cd openssl-3.4.2 && \
if [ ! -f ../openssl-install/lib64/libssl.a ] || [ ! -f ../openssl-install/lib64/libcrypto.a ]; then \
echo "Configuring minimal OpenSSL..."; \
make distclean >/dev/null 2>&1 || true; \
./Configure linux-x86_64 \
--prefix=$(PWD)/openssl-install \
--openssldir=$(PWD)/openssl-install/ssl \
no-shared no-dso no-apps no-docs \
no-ssl3 no-tls1 no-tls1_1 \
no-engine no-comp no-legacy \
no-gost no-idea no-seed no-md2 no-md4 \
no-mdc2 no-rmd160 no-camellia no-rc5 \
no-bf no-cast no-des \
enable-tls1_2 enable-tls1_3; \
echo "Building minimal OpenSSL libraries..."; \
make -j$(shell nproc 2>/dev/null || echo 4); \
make install_sw >/dev/null 2>&1; \
else \
echo "OpenSSL libraries already exist, skipping build"; \
fi
@echo "x86_64 minimal OpenSSL libraries built successfully"
# Build curl for x86_64 (minimal HTTPS-only build)
$(CURL_LIB): curl-8.15.0/curl-8.15.0/configure $(OPENSSL_LIB_SSL)
@echo "Building minimal curl for x86_64 (HTTPS-only)..."
@cd curl-8.15.0/curl-8.15.0 && \
if [ ! -f ../../curl-install/lib/libcurl.a ]; then \
echo "Configuring minimal curl..."; \
make distclean >/dev/null 2>&1 || true; \
./configure --prefix=$(PWD)/curl-install \
--with-openssl=$(PWD)/openssl-install \
--disable-shared --enable-static \
--disable-ftp --disable-file --disable-ldap --disable-ldaps \
--disable-pop3 --disable-imap --disable-smtp \
--disable-gopher --disable-smb --disable-telnet \
--disable-tftp --disable-dict --disable-rtsp \
--disable-manual --disable-libcurl-option \
--without-libpsl --without-nghttp2 --without-brotli --without-zstd \
--without-libidn2 --without-librtmp --without-libssh2; \
echo "Building minimal curl library..."; \
make -j$(shell nproc 2>/dev/null || echo 4); \
make install >/dev/null 2>&1; \
else \
echo "curl library already exists, skipping build"; \
fi
@echo "x86_64 minimal curl library built successfully"
# Static library - includes secp256k1 and OpenSSL objects for self-contained library
$(STATIC_LIB): $(LIB_OBJECTS) $(SECP256K1_LIB) $(OPENSSL_LIB_SSL) $(OPENSSL_LIB_CRYPTO)
@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 "Extracting OpenSSL objects..."
@mkdir -p .tmp_openssl
@cd .tmp_openssl && $(AR) x ../$(OPENSSL_LIB_SSL)
@cd .tmp_openssl && $(AR) x ../$(OPENSSL_LIB_CRYPTO)
@echo "Combining all objects into $@..."
$(AR) rcs $@ $(LIB_OBJECTS) .tmp_secp256k1/*.o .tmp_openssl/*.o
@rm -rf .tmp_secp256k1 .tmp_openssl
@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 -Isecp256k1/include -Inostr_websocket -I./openssl-install/include -I./curl-install/include
# ARM64 static library - includes secp256k1 objects for self-contained library (OpenSSL handled separately for cross-compile)
$(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 "Note: ARM64 users need to link with OpenSSL separately: -lssl -lcrypto"
@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
@echo "Compiling: $<"
$(CC) $(CFLAGS) $(LOGGING_FLAGS) $(INCLUDES) -c $< -o $@
# ARM64 object files
%.arm64.o: %.c
@echo "Compiling for ARM64: $<"
$(ARM64_CC) $(CFLAGS) $(LOGGING_FLAGS) $(ARM64_INCLUDES) -c $< -o $@
# Examples
examples: $(EXAMPLE_TARGETS)
examples/%: examples/%.c $(STATIC_LIB)
@echo "Building example: $@"
$(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)
debug: clean default
# Install library to system (static only)
install: $(STATIC_LIB)
@echo "Installing static library..."
sudo cp $(STATIC_LIB) /usr/local/lib/
sudo cp nostr_core/nostr_core.h /usr/local/include/
sudo cp nostr_core/nostr_crypto.h /usr/local/include/
# Uninstall library
uninstall:
@echo "Uninstalling library..."
sudo rm -f /usr/local/lib/$(STATIC_LIB)
sudo rm -f /usr/local/include/nostr_core.h
sudo rm -f /usr/local/include/nostr_crypto.h
# Test executables
CRYPTO_TEST_EXEC = tests/nostr_crypto_test
CORE_TEST_EXEC = tests/nostr_core_test
RELAY_POOL_TEST_EXEC = tests/relay_pool_test
EVENT_GEN_TEST_EXEC = tests/test_event_generation
POW_LOOP_TEST_EXEC = tests/test_pow_loop
NIP04_TEST_EXEC = tests/nip04_test
HTTP_TEST_EXEC = tests/http_test
WSS_TEST_EXEC = tests/wss_test
STATIC_LINKING_TEST_EXEC = tests/static_linking_only_test
MAKEFILE_STATIC_TEST_EXEC = tests/makefile_static_test
NIP05_TEST_EXEC = tests/nip05_test
NIP11_TEST_EXEC = tests/nip11_test
ARM64_CRYPTO_TEST_EXEC = tests/nostr_crypto_test_arm64
ARM64_CORE_TEST_EXEC = tests/nostr_core_test_arm64
ARM64_RELAY_POOL_TEST_EXEC = tests/relay_pool_test_arm64
ARM64_NIP04_TEST_EXEC = tests/nip04_test_arm64
# Test compilation flags - matches exact customer usage pattern
TEST_CFLAGS = -Wall -Wextra -std=c99 -g -I. -I./secp256k1/include -I./openssl-install/include
TEST_LDFLAGS = ./libnostr_core.a -lm -static
ARM64_TEST_CFLAGS = -Wall -Wextra -std=c99 -g -I.
ARM64_TEST_LDFLAGS = ./libnostr_core_arm64.a -lm -static
# Build crypto test executable (x86_64)
$(CRYPTO_TEST_EXEC): tests/nostr_crypto_test.c $(STATIC_LIB)
@echo "Building crypto test suite (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build core test executable (x86_64)
$(CORE_TEST_EXEC): tests/nostr_core_test.c $(STATIC_LIB)
@echo "Building core test suite (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build relay pool test executable (x86_64)
$(RELAY_POOL_TEST_EXEC): tests/relay_pool_test.c $(STATIC_LIB)
@echo "Building relay pool test suite (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build event generation test executable (x86_64)
$(EVENT_GEN_TEST_EXEC): tests/test_event_generation.c $(STATIC_LIB)
@echo "Building event generation test suite (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build PoW loop test executable (x86_64)
$(POW_LOOP_TEST_EXEC): tests/test_pow_loop.c $(STATIC_LIB)
@echo "Building PoW loop test program (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build NIP-04 test executable (x86_64)
$(NIP04_TEST_EXEC): tests/nip04_test.c $(STATIC_LIB)
@echo "Building NIP-04 encryption test suite (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build HTTP test executable (x86_64) - Uses customer linking pattern
$(HTTP_TEST_EXEC): tests/http_test.c $(STATIC_LIB)
@echo "Building HTTP/curl compatibility test (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build WebSocket SSL test executable (x86_64)
$(WSS_TEST_EXEC): tests/wss_test.c $(STATIC_LIB)
@echo "Building WebSocket SSL/OpenSSL compatibility test (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build static linking test executable (x86_64)
$(STATIC_LINKING_TEST_EXEC): tests/static_linking_only_test.c $(STATIC_LIB)
@echo "Building static linking verification test (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build Makefile-based static test executable (x86_64) - No library dependency, just parses Makefile
$(MAKEFILE_STATIC_TEST_EXEC): tests/makefile_static_test.c
@echo "Building Makefile-based static configuration test (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ -static
# Build NIP-05 test executable (x86_64) - Uses customer linking pattern
$(NIP05_TEST_EXEC): tests/nip05_test.c $(STATIC_LIB)
@echo "Building NIP-05 identifier verification test (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build NIP-11 test executable (x86_64) - Uses customer linking pattern
$(NIP11_TEST_EXEC): tests/nip11_test.c $(STATIC_LIB)
@echo "Building NIP-11 relay information test (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build simple initialization test executable (x86_64)
tests/simple_init_test: tests/simple_init_test.c $(STATIC_LIB)
@echo "Building simple initialization test program (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build ChaCha20 test executable (x86_64)
tests/chacha20_test: tests/chacha20_test.c $(STATIC_LIB)
@echo "Building ChaCha20 RFC 8439 test suite (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build sync test executable (x86_64)
tests/sync_test: tests/sync_test.c $(STATIC_LIB)
@echo "Building synchronous relay query test program (x86_64)..."
$(CC) $(TEST_CFLAGS) $< -o $@ $(TEST_LDFLAGS)
# Build crypto test ARM64 executable
$(ARM64_CRYPTO_TEST_EXEC): tests/nostr_crypto_test.c $(ARM64_STATIC_LIB)
@echo "Building crypto test suite (ARM64)..."
$(ARM64_CC) $(ARM64_TEST_CFLAGS) $< -o $@ $(ARM64_TEST_LDFLAGS)
# Build core test ARM64 executable
$(ARM64_CORE_TEST_EXEC): tests/nostr_core_test.c $(ARM64_STATIC_LIB)
@echo "Building core test suite (ARM64)..."
$(ARM64_CC) $(ARM64_TEST_CFLAGS) $< -o $@ $(ARM64_TEST_LDFLAGS)
# Build relay pool test ARM64 executable
$(ARM64_RELAY_POOL_TEST_EXEC): tests/relay_pool_test.c $(ARM64_STATIC_LIB)
@echo "Building relay pool test suite (ARM64)..."
$(ARM64_CC) $(ARM64_TEST_CFLAGS) $< -o $@ $(ARM64_TEST_LDFLAGS)
# Build NIP-04 test ARM64 executable
$(ARM64_NIP04_TEST_EXEC): tests/nip04_test.c $(ARM64_STATIC_LIB)
@echo "Building NIP-04 encryption test suite (ARM64)..."
$(ARM64_CC) $(ARM64_TEST_CFLAGS) $< -o $@ $(ARM64_TEST_LDFLAGS)
# Run crypto tests (x86_64)
test-crypto: $(CRYPTO_TEST_EXEC)
@echo "Running crypto tests (x86_64)..."
./$(CRYPTO_TEST_EXEC)
# Run core tests (x86_64)
test-core: $(CORE_TEST_EXEC)
@echo "Running core tests (x86_64)..."
./$(CORE_TEST_EXEC)
# Run relay pool tests (x86_64)
test-relay-pool: $(RELAY_POOL_TEST_EXEC)
@echo "Running relay pool tests (x86_64)..."
./$(RELAY_POOL_TEST_EXEC)
# Run NIP-04 tests (x86_64)
test-nip04: $(NIP04_TEST_EXEC)
@echo "Running NIP-04 encryption tests (x86_64)..."
./$(NIP04_TEST_EXEC)
# Run HTTP tests (x86_64)
test-http: $(HTTP_TEST_EXEC)
@echo "Running HTTP/curl compatibility tests (x86_64)..."
./$(HTTP_TEST_EXEC)
# Run WebSocket SSL tests (x86_64)
test-wss: $(WSS_TEST_EXEC)
@echo "Running WebSocket SSL/OpenSSL compatibility tests (x86_64)..."
./$(WSS_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 Makefile-based static configuration test (x86_64)
test-makefile-static: $(MAKEFILE_STATIC_TEST_EXEC)
@echo "Running Makefile-based static configuration test (x86_64)..."
./$(MAKEFILE_STATIC_TEST_EXEC)
# Run NIP-05 tests (x86_64)
test-nip05: $(NIP05_TEST_EXEC)
@echo "Running NIP-05 identifier verification tests (x86_64)..."
./$(NIP05_TEST_EXEC)
# Run NIP-11 tests (x86_64)
test-nip11: $(NIP11_TEST_EXEC)
@echo "Running NIP-11 relay information tests (x86_64)..."
./$(NIP11_TEST_EXEC)
# Run all test suites (x86_64)
test: test-crypto test-core test-relay-pool test-nip04 test-http test-wss test-static-linking test-makefile-static test-nip05 test-nip11
# Run crypto tests ARM64 (requires qemu-user-static or ARM64 system)
test-crypto-arm64: $(ARM64_CRYPTO_TEST_EXEC)
@echo "Running crypto tests (ARM64)..."
@if command -v qemu-aarch64-static >/dev/null 2>&1; then \
echo "Using qemu-aarch64-static to run ARM64 binary..."; \
qemu-aarch64-static ./$(ARM64_CRYPTO_TEST_EXEC); \
else \
echo "qemu-aarch64-static not found. ARM64 binary built but cannot run on x86_64."; \
echo "To run: copy $(ARM64_CRYPTO_TEST_EXEC) to ARM64 system and execute."; \
file ./$(ARM64_CRYPTO_TEST_EXEC); \
fi
# Run core tests ARM64 (requires qemu-user-static or ARM64 system)
test-core-arm64: $(ARM64_CORE_TEST_EXEC)
@echo "Running core tests (ARM64)..."
@if command -v qemu-aarch64-static >/dev/null 2>&1; then \
echo "Using qemu-aarch64-static to run ARM64 binary..."; \
qemu-aarch64-static ./$(ARM64_CORE_TEST_EXEC); \
else \
echo "qemu-aarch64-static not found. ARM64 binary built but cannot run on x86_64."; \
echo "To run: copy $(ARM64_CORE_TEST_EXEC) to ARM64 system and execute."; \
file ./$(ARM64_CORE_TEST_EXEC); \
fi
# Run relay pool tests ARM64 (requires qemu-user-static or ARM64 system)
test-relay-pool-arm64: $(ARM64_RELAY_POOL_TEST_EXEC)
@echo "Running relay pool tests (ARM64)..."
@if command -v qemu-aarch64-static >/dev/null 2>&1; then \
echo "Using qemu-aarch64-static to run ARM64 binary..."; \
qemu-aarch64-static ./$(ARM64_RELAY_POOL_TEST_EXEC); \
else \
echo "qemu-aarch64-static not found. ARM64 binary built but cannot run on x86_64."; \
echo "To run: copy $(ARM64_RELAY_POOL_TEST_EXEC) to ARM64 system and execute."; \
file ./$(ARM64_RELAY_POOL_TEST_EXEC); \
fi
# Run all test suites on ARM64
test-arm64: test-crypto-arm64 test-core-arm64 test-relay-pool-arm64
# Run tests on both architectures
test-all: test test-arm64
# Test the library with simple example
test-simple: examples/simple_keygen
@echo "Running simple key generation test..."
./examples/simple_keygen
# Clean build artifacts
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 .tmp_openssl
rm -rf secp256k1/build_arm64 secp256k1/install_arm64
# Create distribution package
dist: clean
@echo "Creating distribution package..."
mkdir -p dist/nostr_core
cp -r *.h *.c Makefile examples/ tests/ README.md LICENSE dist/nostr_core/ 2>/dev/null || true
cd dist && tar -czf nostr_core.tar.gz nostr_core/
@echo "Distribution package created: dist/nostr_core.tar.gz"
# Help
help:
@echo "NOSTR Core Library Build System"
@echo "==============================="
@echo ""
@echo "Available targets:"
@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"
@echo " install - Install static library to system (/usr/local)"
@echo " uninstall - Remove library from system"
@echo " clean - Remove build artifacts"
@echo " dist - Create distribution package"
@echo " help - Show this help"
@echo ""
@echo "Library outputs (static only, self-contained):"
@echo " $(STATIC_LIB) - x86_64 static library (includes secp256k1 + OpenSSL)"
@echo " $(ARM64_STATIC_LIB) - ARM64 static library (includes secp256k1, needs OpenSSL)"
@echo ""
@echo "x64 library: Users only need to link with the library + -lm"
@echo "ARM64 library: Users need to link with the library + -lssl -lcrypto -lm"
.PHONY: default all x64 x64-only arm64 arm64-all arm64-only debug examples test test-crypto install uninstall clean dist help

169
build.sh
View File

@ -5,28 +5,51 @@
set -e # Exit on error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Color constants
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
BLUE='\033[34m'
BOLD='\033[1m'
RESET='\033[0m'
# Function to print colored output
# Detect if we should use colors (terminal output and not piped)
USE_COLORS=true
if [ ! -t 1 ] || [ "$NO_COLOR" = "1" ]; then
USE_COLORS=false
fi
# Function to print output with colors
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
if [ "$USE_COLORS" = true ]; then
echo -e "${BLUE}[INFO]${RESET} $1"
else
echo "[INFO] $1"
fi
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
if [ "$USE_COLORS" = true ]; then
echo -e "${GREEN}${BOLD}[SUCCESS]${RESET} $1"
else
echo "[SUCCESS] $1"
fi
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
if [ "$USE_COLORS" = true ]; then
echo -e "${YELLOW}[WARNING]${RESET} $1"
else
echo "[WARNING] $1"
fi
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
if [ "$USE_COLORS" = true ]; then
echo -e "${RED}${BOLD}[ERROR]${RESET} $1"
else
echo "[ERROR] $1"
fi
}
# Default values
@ -35,6 +58,7 @@ FORCE_NIPS=""
VERBOSE=false
HELP=false
BUILD_TESTS=false
NO_COLOR_FLAG=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
@ -55,10 +79,14 @@ while [[ $# -gt 0 ]]; do
VERBOSE=true
shift
;;
--tests)
--tests|-t)
BUILD_TESTS=true
shift
;;
--no-color)
NO_COLOR_FLAG=true
shift
;;
--help|-h)
HELP=true
shift
@ -71,6 +99,11 @@ while [[ $# -gt 0 ]]; do
esac
done
# Apply no-color flag
if [ "$NO_COLOR_FLAG" = true ]; then
USE_COLORS=false
fi
# Show help
if [ "$HELP" = true ]; then
echo "NOSTR Core Library - Customer Build Script"
@ -85,8 +118,9 @@ if [ "$HELP" = true ]; then
echo "Options:"
echo " --nips=1,5,6,19 Force specific NIPs (comma-separated)"
echo " --nips=all Include all available NIPs"
echo " --tests Build all test programs in tests/ directory"
echo " --tests, -t Build all test programs in tests/ directory"
echo " --verbose, -v Verbose output"
echo " --no-color Disable colored output"
echo " --help, -h Show this help"
echo ""
echo "Auto-Detection:"
@ -107,17 +141,25 @@ if [ "$HELP" = true ]; then
echo " $0 x64 # Auto-detect NIPs, build for x64"
echo " $0 --nips=1,6,19 # Force NIPs 1,6,19 only"
echo " $0 arm64 --nips=all # Build all NIPs for ARM64"
echo " $0 -t # Build library and all tests"
exit 0
fi
print_info "NOSTR Core Library - Customer Build Script"
print_info "Auto-detecting needed NIPs from your source code..."
# Detect NIPs from source files
###########################################################################################
###########################################################################################
############ AUTODETECT NIPS FROM SOURCE FILES
###########################################################################################
###########################################################################################
NEEDED_NIPS=""
if [ -n "$FORCE_NIPS" ]; then
if [ "$FORCE_NIPS" = "all" ]; then
NEEDED_NIPS="001 005 006 011 013 019 044"
NEEDED_NIPS="001 004 005 006 011 013 019 044"
print_info "Forced: Building all available NIPs"
else
# Convert comma-separated list to space-separated with 3-digit format
@ -152,12 +194,26 @@ else
fi
fi
# If building tests, include all NIPs to ensure test compatibility
if [ "$BUILD_TESTS" = true ] && [ -z "$FORCE_NIPS" ]; then
NEEDED_NIPS="001 005 006 011 013 019 044"
print_info "Building tests - including all available NIPs for test compatibility"
fi
# Ensure NIP-001 is always included (required for core functionality)
if ! echo "$NEEDED_NIPS" | grep -q "001"; then
NEEDED_NIPS="001 $NEEDED_NIPS"
print_info "Added NIP-001 (required for core functionality)"
fi
###########################################################################################
###########################################################################################
############ AUTODETECT SYSTEM ARCHITECTURE
###########################################################################################
###########################################################################################
# Determine architecture
if [ -z "$ARCHITECTURE" ]; then
ARCH=$(uname -m)
@ -202,13 +258,20 @@ if ! command -v $CC &> /dev/null; then
exit 1
fi
# Build source file list - Core crypto dependencies
SOURCES="nostr_core/crypto/nostr_crypto.c"
SOURCES="$SOURCES nostr_core/crypto/nostr_secp256k1.c"
###########################################################################################
###########################################################################################
############ ADD CORE DEPENDENCIES THAT NEED TO BE BUILT TO THE $SOURCES VARIABLE
###########################################################################################
###########################################################################################
SOURCES="nostr_core/crypto/nostr_secp256k1.c"
SOURCES="$SOURCES nostr_core/crypto/nostr_aes.c"
SOURCES="$SOURCES nostr_core/crypto/nostr_chacha20.c"
SOURCES="$SOURCES cjson/cJSON.c"
SOURCES="$SOURCES nostr_core/utils.c"
SOURCES="$SOURCES nostr_core/nostr_common.c"
# Add secp256k1 library path based on architecture
case $ARCHITECTURE in
@ -227,6 +290,7 @@ for nip in $NEEDED_NIPS; do
SOURCES="$SOURCES $NIP_FILE"
case $nip in
001) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-001(Basic)" ;;
004) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-004(Encrypt)" ;;
005) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-005(DNS)" ;;
006) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-006(Keys)" ;;
011) NIP_DESCRIPTIONS="$NIP_DESCRIPTIONS NIP-011(Relay-Info)" ;;
@ -259,7 +323,14 @@ if [ "$VERBOSE" = true ]; then
print_info "Flags: $CFLAGS $INCLUDES"
fi
# Compile each source file to object file
###########################################################################################
###########################################################################################
############ COMPILE EACH SOURCE FROM $SOURCES INTO A .o FILE
###########################################################################################
###########################################################################################
OBJECTS=""
for source in $SOURCES; do
if [ -f "$source" ]; then
@ -270,7 +341,11 @@ for source in $SOURCES; do
print_info "Compiling: $source -> $obj_name"
fi
#################################################
# THE ACTUAL COMMAND TO COMPILE .c FILES
#################################################
$CC $CFLAGS $INCLUDES -c "$source" -o "$obj_name"
if [ $? -ne 0 ]; then
print_error "Failed to compile $source"
exit 1
@ -281,7 +356,13 @@ for source in $SOURCES; do
fi
done
# Create self-contained static library (extract all objects first)
###########################################################################################
###########################################################################################
############ CREATE THE FINAL STATIC LIBRARY FOR THE PROJECT: libnostr_core_XX.a
############ BY LINKING IN ALL OUR .o FILES THAT ARE REQUESTED TO BE ADDED.
###########################################################################################
###########################################################################################
print_info "Creating self-contained static library: $OUTPUT"
# Create temporary directories for extracting objects
@ -316,12 +397,26 @@ fi
if [ "$VERBOSE" = true ]; then
print_info "Combining all objects into self-contained library..."
fi
ar rcs "$OUTPUT" $OBJECTS "$TMP_SECP256K1"/*.o "$TMP_OPENSSL"/*.o "$TMP_CURL"/*.o 2>/dev/null
#########################################################
### THE ACTUAL COMMAND TO LINK .o FILES INTO A .a FILE
#########################################################
ar rcs "$OUTPUT" $OBJECTS "$TMP_SECP256K1"/*.o "$TMP_OPENSSL"/*.o "$TMP_CURL"/*.o
AR_RESULT=$?
# Cleanup temporary directories
rm -rf "$TMP_SECP256K1" "$TMP_OPENSSL" "$TMP_CURL"
if [ $? -eq 0 ]; then
###########################################################################################
###########################################################################################
############ IF THE LINKING OCCURED SUCCESSFULLY, BUILD THE TEST FILE EXECUTABLES
############ BY LINKING IN ALL OUR .o FILES THAT ARE REQUESTED TO BE ADDED.
###########################################################################################
###########################################################################################
if [ $AR_RESULT -eq 0 ]; then
# Cleanup object files
rm -f $OBJECTS
@ -347,36 +442,18 @@ if [ $? -eq 0 ]; then
print_info "Building test: $test_name"
# Determine linking requirements based on file content
LINK_FLAGS="-lm -static"
# Simple test compilation - everything is in our fat library
LINK_FLAGS="-lz -ldl -lpthread -lm -static"
# Always include secp256k1 library if available
if [ -f "$SECP256K1_LIB" ]; then
LINK_FLAGS="$SECP256K1_LIB $LINK_FLAGS"
fi
# Check if test needs curl/SSL libraries (NIP-05/NIP-11)
if grep -q -E 'nip005|nip011|curl/curl\.h' "$test_file" 2>/dev/null; then
# Check if curl and SSL libraries are available
if [ -f "./curl-install/lib/libcurl.a" ] && [ -f "./openssl-install/lib64/libssl.a" ]; then
LINK_FLAGS="./curl-install/lib/libcurl.a ./openssl-install/lib64/libssl.a ./openssl-install/lib64/libcrypto.a -lz -ldl -lpthread $LINK_FLAGS"
if [ "$VERBOSE" = true ]; then
print_info " Using full static SSL/curl linking for $test_name"
fi
else
print_warning " Test $test_name needs curl/SSL but libraries not found - using basic linking"
fi
fi
# Compile the test
if [ "$VERBOSE" = true ]; then
print_info " Command: $CC $CFLAGS $INCLUDES \"$test_file\" -o \"$test_exe\" ./$OUTPUT $LINK_FLAGS"
fi
if $CC $CFLAGS $INCLUDES "$test_file" -o "$test_exe" "./$OUTPUT" $LINK_FLAGS 2>/dev/null; then
if $CC $CFLAGS $INCLUDES "$test_file" -o "$test_exe" "./$OUTPUT" $LINK_FLAGS; then
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
print_success "Built $test_name"
if [ "$VERBOSE" = true ]; then
print_success " Built: $test_exe"
print_info " Executable: $test_exe"
fi
else
print_error " Failed to build: $test_name"
@ -394,7 +471,7 @@ if [ $? -eq 0 ]; then
fi
echo "Usage in your project:"
echo " gcc your_app.c $OUTPUT -lz -ldl -lpthread -o your_app"
echo " gcc your_app.c $OUTPUT -lz -ldl -lpthread -lm -o your_app"
echo ""
else
print_error "Failed to create static library"

View File

@ -1,40 +0,0 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
# Find required dependencies
find_dependency(Threads REQUIRED)
# Include targets
include("${CMAKE_CURRENT_LIST_DIR}/nostr_core-targets.cmake")
# Set variables for backward compatibility
set(NOSTR_CORE_FOUND TRUE)
set(NOSTR_CORE_VERSION "@PROJECT_VERSION@")
# Check which libraries are available
set(NOSTR_CORE_STATIC_AVAILABLE FALSE)
set(NOSTR_CORE_SHARED_AVAILABLE FALSE)
if(TARGET nostr_core::static)
set(NOSTR_CORE_STATIC_AVAILABLE TRUE)
endif()
if(TARGET nostr_core::shared)
set(NOSTR_CORE_SHARED_AVAILABLE TRUE)
endif()
# Provide convenient variables
if(NOSTR_CORE_STATIC_AVAILABLE)
set(NOSTR_CORE_LIBRARIES nostr_core::static)
elseif(NOSTR_CORE_SHARED_AVAILABLE)
set(NOSTR_CORE_LIBRARIES nostr_core::shared)
endif()
set(NOSTR_CORE_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@/nostr")
# Feature information
set(NOSTR_CORE_ENABLE_WEBSOCKETS @NOSTR_ENABLE_WEBSOCKETS@)
set(NOSTR_CORE_USE_MBEDTLS @NOSTR_USE_MBEDTLS@)
check_required_components(nostr_core)

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,186 +0,0 @@
/*
* NOSTR Crypto - Self-contained cryptographic functions
*
* Embedded implementations of crypto primitives needed for NOSTR
* No external dependencies except standard C library
*/
#ifndef NOSTR_CRYPTO_H
#define NOSTR_CRYPTO_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// =============================================================================
// CORE CRYPTO FUNCTIONS
// =============================================================================
// Initialize crypto subsystem
int nostr_crypto_init(void);
// Cleanup crypto subsystem
void nostr_crypto_cleanup(void);
// SHA-256 hash function
int nostr_sha256(const unsigned char* data, size_t len, unsigned char* hash);
// HMAC-SHA256
int nostr_hmac_sha256(const unsigned char* key, size_t key_len,
const unsigned char* data, size_t data_len,
unsigned char* output);
// HMAC-SHA512
int nostr_hmac_sha512(const unsigned char* key, size_t key_len,
const unsigned char* data, size_t data_len,
unsigned char* output);
// PBKDF2 with HMAC-SHA512
int nostr_pbkdf2_hmac_sha512(const unsigned char* password, size_t password_len,
const unsigned char* salt, size_t salt_len,
int iterations,
unsigned char* output, size_t output_len);
// SHA-512 implementation (for testing)
int nostr_sha512(const unsigned char* data, size_t len, unsigned char* hash);
// =============================================================================
// SECP256K1 ELLIPTIC CURVE FUNCTIONS
// =============================================================================
// Verify private key is valid
int nostr_ec_private_key_verify(const unsigned char* private_key);
// Generate public key from private key
int nostr_ec_public_key_from_private_key(const unsigned char* private_key,
unsigned char* public_key);
// Sign data with ECDSA
int nostr_ec_sign(const unsigned char* private_key,
const unsigned char* hash,
unsigned char* signature);
// RFC 6979 deterministic nonce generation
int nostr_rfc6979_generate_k(const unsigned char* private_key,
const unsigned char* message_hash,
unsigned char* k_out);
// =============================================================================
// HKDF KEY DERIVATION FUNCTIONS
// =============================================================================
// HKDF Extract step
int nostr_hkdf_extract(const unsigned char* salt, size_t salt_len,
const unsigned char* ikm, size_t ikm_len,
unsigned char* prk);
// HKDF Expand step
int nostr_hkdf_expand(const unsigned char* prk, size_t prk_len,
const unsigned char* info, size_t info_len,
unsigned char* okm, size_t okm_len);
// HKDF (Extract + Expand)
int nostr_hkdf(const unsigned char* salt, size_t salt_len,
const unsigned char* ikm, size_t ikm_len,
const unsigned char* info, size_t info_len,
unsigned char* okm, size_t okm_len);
// ECDH shared secret computation (for debugging)
int ecdh_shared_secret(const unsigned char* private_key,
const unsigned char* public_key_x,
unsigned char* shared_secret);
// Base64 encoding function (for debugging)
size_t base64_encode(const unsigned char* data, size_t len, char* output, size_t output_size);
// =============================================================================
// NIP-04 AND NIP-44 ENCRYPTION FUNCTIONS
// =============================================================================
// Note: NOSTR_NIP04_MAX_PLAINTEXT_SIZE already defined in nostr_core.h
#define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 65536
// NIP-04 encryption (AES-256-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);
// NIP-04 decryption
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 encryption (ChaCha20-Poly1305)
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);
// NIP-44 encryption with fixed nonce (for testing)
int nostr_nip44_encrypt_with_nonce(const unsigned char* sender_private_key,
const unsigned char* recipient_public_key,
const char* plaintext,
const unsigned char* nonce,
char* output,
size_t output_size);
// NIP-44 decryption
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);
// =============================================================================
// BIP39 MNEMONIC FUNCTIONS
// =============================================================================
// Generate mnemonic from entropy
int nostr_bip39_mnemonic_from_bytes(const unsigned char* entropy, size_t entropy_len,
char* mnemonic);
// Validate mnemonic
int nostr_bip39_mnemonic_validate(const char* mnemonic);
// Convert mnemonic to seed
int nostr_bip39_mnemonic_to_seed(const char* mnemonic, const char* passphrase,
unsigned char* seed, size_t seed_len);
// =============================================================================
// BIP32 HD WALLET FUNCTIONS
// =============================================================================
typedef struct {
unsigned char private_key[32];
unsigned char public_key[33];
unsigned char chain_code[32];
uint32_t depth;
uint32_t parent_fingerprint;
uint32_t child_number;
} nostr_hd_key_t;
// Create master key from seed
int nostr_bip32_key_from_seed(const unsigned char* seed, size_t seed_len,
nostr_hd_key_t* master_key);
// Derive child key from parent
int nostr_bip32_derive_child(const nostr_hd_key_t* parent_key, uint32_t child_number,
nostr_hd_key_t* child_key);
// Derive key from path
int nostr_bip32_derive_path(const nostr_hd_key_t* master_key, const uint32_t* path,
size_t path_len, nostr_hd_key_t* derived_key);
#ifdef __cplusplus
}
#endif
#endif // NOSTR_CRYPTO_H

View File

@ -5,57 +5,18 @@
*/
#include "nip001.h"
#include "nostr_crypto.h"
#include "utils.h"
#include "../cjson/cJSON.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../nostr_core/nostr_common.h"
// Declare utility functions
void nostr_bytes_to_hex(const unsigned char* bytes, size_t len, char* hex);
int nostr_hex_to_bytes(const char* hex, unsigned char* bytes, size_t len);
/**
* Initialize the NOSTR library
*/
int nostr_init(void) {
if (nostr_crypto_init() != 0) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
return NOSTR_SUCCESS;
}
/**
* Cleanup the NOSTR library
*/
void nostr_cleanup(void) {
nostr_crypto_cleanup();
}
/**
* Convert error code to human-readable string
*/
const char* nostr_strerror(int error_code) {
switch (error_code) {
case NOSTR_SUCCESS: return "Success";
case NOSTR_ERROR_INVALID_INPUT: return "Invalid input";
case NOSTR_ERROR_CRYPTO_FAILED: return "Cryptographic operation failed";
case NOSTR_ERROR_MEMORY_FAILED: return "Memory allocation failed";
case NOSTR_ERROR_IO_FAILED: return "I/O operation failed";
case NOSTR_ERROR_NETWORK_FAILED: return "Network operation failed";
case NOSTR_ERROR_NIP04_INVALID_FORMAT: return "NIP-04 invalid format";
case NOSTR_ERROR_NIP04_DECRYPT_FAILED: return "NIP-04 decryption failed";
case NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL: return "NIP-04 buffer too small";
case NOSTR_ERROR_NIP05_INVALID_IDENTIFIER: return "NIP-05: Invalid identifier format";
case NOSTR_ERROR_NIP05_HTTP_FAILED: return "NIP-05: HTTP request failed";
case NOSTR_ERROR_NIP05_JSON_PARSE_FAILED: return "NIP-05: JSON parsing failed";
case NOSTR_ERROR_NIP05_NAME_NOT_FOUND: return "NIP-05: Name not found in .well-known";
case NOSTR_ERROR_NIP05_PUBKEY_MISMATCH: return "NIP-05: Public key mismatch";
default: return "Unknown error";
}
}
/**
* Create and sign a NOSTR event
*/

View File

@ -11,26 +11,8 @@
#include <time.h>
#include "../cjson/cJSON.h"
// Error codes
#define NOSTR_SUCCESS 0
#define NOSTR_ERROR_INVALID_INPUT -1
#define NOSTR_ERROR_CRYPTO_FAILED -2
#define NOSTR_ERROR_MEMORY_FAILED -3
#define NOSTR_ERROR_IO_FAILED -4
#define NOSTR_ERROR_NETWORK_FAILED -5
#define NOSTR_ERROR_NIP04_INVALID_FORMAT -10
#define NOSTR_ERROR_NIP04_DECRYPT_FAILED -11
#define NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL -12
#define NOSTR_ERROR_NIP05_INVALID_IDENTIFIER -20
#define NOSTR_ERROR_NIP05_HTTP_FAILED -21
#define NOSTR_ERROR_NIP05_JSON_PARSE_FAILED -22
#define NOSTR_ERROR_NIP05_NAME_NOT_FOUND -23
#define NOSTR_ERROR_NIP05_PUBKEY_MISMATCH -24
// Function declarations
int nostr_init(void);
void nostr_cleanup(void);
const char* nostr_strerror(int error_code);
cJSON* nostr_create_and_sign_event(int kind, const char* content, cJSON* tags, const unsigned char* private_key, time_t timestamp);
#endif // NIP001_H

336
nostr_core/nip004.c Normal file
View File

@ -0,0 +1,336 @@
/*
* NIP-04: Encrypted Direct Message Implementation
* https://github.com/nostr-protocol/nips/blob/master/04.md
*/
#include "nip004.h"
#include "utils.h"
#include "nostr_common.h"
#include "crypto/nostr_secp256k1.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Include our AES implementation
#include "crypto/nostr_aes.h"
// Forward declarations for internal functions
static int aes_cbc_encrypt(const unsigned char* key, const unsigned char* iv,
const unsigned char* input, size_t input_len,
unsigned char* output);
static int aes_cbc_decrypt(const unsigned char* key, const unsigned char* iv,
const unsigned char* input, size_t input_len,
unsigned char* output);
static size_t pkcs7_pad(unsigned char* data, size_t data_len, size_t block_size);
static size_t pkcs7_unpad(unsigned char* data, size_t data_len);
// Memory clearing utility
static void memory_clear(const void *p, size_t len) {
if (p && len) {
memset((void *)p, 0, len);
}
}
// =============================================================================
// AES-256-CBC ENCRYPTION/DECRYPTION USING TINYAES
// =============================================================================
static int aes_cbc_encrypt(const unsigned char* key, const unsigned char* iv,
const unsigned char* input, size_t input_len,
unsigned char* output) {
if (!key || !iv || !input || !output || input_len % 16 != 0) {
return -1;
}
// Initialize AES context with key and IV
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
// Copy input to output (tinyAES works in-place)
memcpy(output, input, input_len);
// Encrypt using AES-256-CBC
AES_CBC_encrypt_buffer(&ctx, output, input_len);
return 0;
}
static int aes_cbc_decrypt(const unsigned char* key, const unsigned char* iv,
const unsigned char* input, size_t input_len,
unsigned char* output) {
if (!key || !iv || !input || !output || input_len % 16 != 0) {
return -1;
}
// Initialize AES context with key and IV
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
// Copy input to output (tinyAES works in-place)
memcpy(output, input, input_len);
// Decrypt using AES-256-CBC
AES_CBC_decrypt_buffer(&ctx, output, input_len);
return 0;
}
// PKCS#7 padding functions
static size_t pkcs7_pad(unsigned char* data, size_t data_len, size_t block_size) {
size_t padding = block_size - (data_len % block_size);
for (size_t i = 0; i < padding; i++) {
data[data_len + i] = (unsigned char)padding;
}
return data_len + padding;
}
static size_t pkcs7_unpad(unsigned char* data, size_t data_len) {
if (data_len == 0) return 0;
unsigned char padding = data[data_len - 1];
if (padding == 0 || padding > 16) return 0; // Invalid padding
// Verify padding
for (size_t i = data_len - padding; i < data_len; i++) {
if (data[i] != padding) return 0; // Invalid padding
}
return data_len - padding;
}
// =============================================================================
// NIP-04 IMPLEMENTATION
// =============================================================================
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) {
if (!sender_private_key || !recipient_public_key || !plaintext || !output) {
return NOSTR_ERROR_INVALID_INPUT;
}
size_t plaintext_len = strlen(plaintext);
if (plaintext_len > NOSTR_NIP04_MAX_PLAINTEXT_SIZE) {
return NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL;
}
// FIX: Calculate final size requirements EARLY before any allocations
// CRITICAL: Account for PKCS#7 padding which ALWAYS adds 1-16 bytes
// If plaintext_len is a multiple of 16, PKCS#7 adds a full 16-byte block
size_t padded_len = ((plaintext_len / 16) + 1) * 16; // Always add one full block for PKCS#7
size_t ciphertext_b64_max = ((padded_len + 2) / 3) * 4 + 1;
size_t iv_b64_max = ((16 + 2) / 3) * 4 + 1; // Always 25 bytes
size_t estimated_result_len = ciphertext_b64_max + 4 + iv_b64_max; // +4 for "?iv="
// FIX: Check output buffer size BEFORE doing any work
if (estimated_result_len > output_size) {
return NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL;
}
// Step 1: Compute ECDH shared secret
unsigned char shared_secret[32];
if (ecdh_shared_secret(sender_private_key, recipient_public_key, shared_secret) != 0) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 2: Generate random IV (16 bytes)
unsigned char iv[16];
if (nostr_secp256k1_get_random_bytes(iv, 16) != 1) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 3: Pad plaintext using PKCS#7
unsigned char* padded_data = malloc(padded_len);
if (!padded_data) {
return NOSTR_ERROR_MEMORY_FAILED;
}
memcpy(padded_data, plaintext, plaintext_len);
size_t actual_padded_len = pkcs7_pad(padded_data, plaintext_len, 16);
// Step 4: Encrypt using AES-256-CBC
unsigned char* ciphertext = malloc(padded_len);
if (!ciphertext) {
free(padded_data);
return NOSTR_ERROR_MEMORY_FAILED;
}
if (aes_cbc_encrypt(shared_secret, iv, padded_data, actual_padded_len, ciphertext) != 0) {
free(padded_data);
free(ciphertext);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 5: Base64 encode ciphertext and IV
size_t ciphertext_b64_len = ((actual_padded_len + 2) / 3) * 4 + 1;
size_t iv_b64_len = ((16 + 2) / 3) * 4 + 1;
char* ciphertext_b64 = malloc(ciphertext_b64_len);
char* iv_b64 = malloc(iv_b64_len);
if (!ciphertext_b64 || !iv_b64) {
free(padded_data);
free(ciphertext);
free(ciphertext_b64);
free(iv_b64);
return NOSTR_ERROR_MEMORY_FAILED;
}
// FIX: Pass buffer sizes to base64_encode and check for success
size_t ct_b64_len = base64_encode(ciphertext, actual_padded_len, ciphertext_b64, ciphertext_b64_len);
size_t iv_b64_len_actual = base64_encode(iv, 16, iv_b64, iv_b64_len);
// FIX: Check if encoding succeeded
if (ct_b64_len == 0 || iv_b64_len_actual == 0) {
free(padded_data);
free(ciphertext);
free(ciphertext_b64);
free(iv_b64);
memory_clear(shared_secret, 32);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 6: Format as "ciphertext?iv=iv_base64" - size check moved earlier, now guaranteed to fit
size_t result_len = ct_b64_len + 4 + iv_b64_len_actual + 1; // +4 for "?iv=", +1 for null
if (result_len > output_size) {
free(padded_data);
free(ciphertext);
free(ciphertext_b64);
free(iv_b64);
memory_clear(shared_secret, 32);
return NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL;
}
snprintf(output, output_size, "%s?iv=%s", ciphertext_b64, iv_b64);
// Cleanup
memory_clear(shared_secret, 32);
memory_clear(padded_data, padded_len);
memory_clear(ciphertext, padded_len);
free(padded_data);
free(ciphertext);
free(ciphertext_b64);
free(iv_b64);
return NOSTR_SUCCESS;
}
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) {
if (!recipient_private_key || !sender_public_key || !encrypted_data || !output) {
return NOSTR_ERROR_INVALID_INPUT;
}
// Step 1: Parse encrypted data format "ciphertext?iv=iv_base64"
char* separator = strstr(encrypted_data, "?iv=");
if (!separator) {
return NOSTR_ERROR_NIP04_INVALID_FORMAT;
}
size_t ciphertext_b64_len = separator - encrypted_data;
const char* iv_b64 = separator + 4; // Skip "?iv="
if (ciphertext_b64_len == 0 || strlen(iv_b64) == 0) {
return NOSTR_ERROR_NIP04_INVALID_FORMAT;
}
// Step 2: Create null-terminated copy of ciphertext base64
char* ciphertext_b64 = malloc(ciphertext_b64_len + 1);
if (!ciphertext_b64) {
return NOSTR_ERROR_MEMORY_FAILED;
}
memcpy(ciphertext_b64, encrypted_data, ciphertext_b64_len);
ciphertext_b64[ciphertext_b64_len] = '\0';
// Step 3: Calculate proper buffer sizes for decoded data
// Base64 decoding: 4 chars -> 3 bytes, so max decoded size is (len * 3) / 4
size_t max_ciphertext_len = ((ciphertext_b64_len + 3) / 4) * 3;
size_t max_iv_len = ((strlen(iv_b64) + 3) / 4) * 3;
// Allocate buffers with proper sizes
unsigned char* ciphertext = malloc(max_ciphertext_len);
unsigned char* iv_buffer = malloc(max_iv_len);
if (!ciphertext || !iv_buffer) {
free(ciphertext_b64);
free(ciphertext);
free(iv_buffer);
return NOSTR_ERROR_MEMORY_FAILED;
}
// Step 4: Base64 decode ciphertext and IV
size_t ciphertext_len = base64_decode(ciphertext_b64, ciphertext);
size_t iv_len = base64_decode(iv_b64, iv_buffer);
if (ciphertext_len == 0 || iv_len != 16 || ciphertext_len % 16 != 0) {
free(ciphertext_b64);
free(ciphertext);
free(iv_buffer);
return NOSTR_ERROR_NIP04_INVALID_FORMAT;
}
// Copy IV to fixed-size buffer for safety
unsigned char iv[16];
memcpy(iv, iv_buffer, 16);
free(iv_buffer);
// Step 5: Compute ECDH shared secret
unsigned char shared_secret[32];
if (ecdh_shared_secret(recipient_private_key, sender_public_key, shared_secret) != 0) {
free(ciphertext_b64);
free(ciphertext);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 6: Decrypt using AES-256-CBC
unsigned char* plaintext_padded = malloc(ciphertext_len);
if (!plaintext_padded) {
free(ciphertext_b64);
free(ciphertext);
return NOSTR_ERROR_MEMORY_FAILED;
}
if (aes_cbc_decrypt(shared_secret, iv, ciphertext, ciphertext_len, plaintext_padded) != 0) {
free(ciphertext_b64);
free(ciphertext);
free(plaintext_padded);
return NOSTR_ERROR_NIP04_DECRYPT_FAILED;
}
// Step 7: Remove PKCS#7 padding
size_t plaintext_len = pkcs7_unpad(plaintext_padded, ciphertext_len);
if (plaintext_len == 0 || plaintext_len > ciphertext_len) {
free(ciphertext_b64);
free(ciphertext);
free(plaintext_padded);
return NOSTR_ERROR_NIP04_DECRYPT_FAILED;
}
// Step 8: Copy to output buffer and null-terminate
if (plaintext_len + 1 > output_size) {
free(ciphertext_b64);
free(ciphertext);
free(plaintext_padded);
return NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL;
}
memcpy(output, plaintext_padded, plaintext_len);
output[plaintext_len] = '\0';
// Cleanup
memory_clear(shared_secret, 32);
memory_clear(plaintext_padded, ciphertext_len);
free(ciphertext_b64);
free(ciphertext);
free(plaintext_padded);
return NOSTR_SUCCESS;
}

56
nostr_core/nip004.h Normal file
View File

@ -0,0 +1,56 @@
/*
* NIP-04: Encrypted Direct Message
* https://github.com/nostr-protocol/nips/blob/master/04.md
*/
#ifndef NOSTR_NIP004_H
#define NOSTR_NIP004_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// NIP-04 constants
// #define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 65535
// NIP-04 Constants
// #define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 16777216 // 16MB
// #define NOSTR_NIP04_MAX_ENCRYPTED_SIZE 22369621 // ~21.3MB (accounts for base64 overhead + IV)
/**
* NIP-04: Encrypt a message using ECDH + AES-256-CBC
*
* @param sender_private_key 32-byte sender private key
* @param recipient_public_key 32-byte recipient public key (x-only)
* @param plaintext Message to encrypt
* @param output Buffer for encrypted output (format: "ciphertext?iv=iv_base64")
* @param output_size Size of output buffer
* @return NOSTR_SUCCESS on success, error code on failure
*/
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);
/**
* NIP-04: Decrypt a message using ECDH + AES-256-CBC
*
* @param recipient_private_key 32-byte recipient private key
* @param sender_public_key 32-byte sender public key (x-only)
* @param encrypted_data Encrypted message (format: "ciphertext?iv=iv_base64")
* @param output Buffer for decrypted plaintext
* @param output_size Size of output buffer
* @return NOSTR_SUCCESS on success, error code on failure
*/
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);
#ifdef __cplusplus
}
#endif
#endif // NOSTR_NIP004_H

View File

@ -4,23 +4,16 @@
#include "nip005.h"
#include "../cjson/cJSON.h"
#include "nostr_common.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h> // For strcasecmp
#include <ctype.h>
#ifndef DISABLE_NIP05
#include <curl/curl.h>
#endif
// Maximum sizes for NIP-05 operations
#define NIP05_MAX_URL_SIZE 512
#define NIP05_MAX_RESPONSE_SIZE 8192
#define NIP05_MAX_IDENTIFIER_SIZE 256
#define NIP05_DEFAULT_TIMEOUT 10
#ifndef DISABLE_NIP05
// Structure for HTTP response handling
typedef struct {
@ -278,7 +271,7 @@ int nostr_nip05_lookup(const char* nip05_identifier, char* pubkey_hex_out,
char local_part[64];
char domain[256];
char url[NIP05_MAX_URL_SIZE];
char url[NOSTR_MAX_URL_SIZE];
// Parse the identifier
int parse_result = nip05_parse_identifier(nip05_identifier, local_part, domain);
@ -348,41 +341,4 @@ int nostr_nip05_verify(const char* nip05_identifier, const char* pubkey_hex,
}
return NOSTR_SUCCESS;
}
#else // DISABLE_NIP05
/**
* Stub implementations when NIP-05 is disabled at compile time
*/
int nostr_nip05_parse_well_known(const char* json_response, const char* local_part,
char* pubkey_hex_out, char*** relays, int* relay_count) {
(void)json_response;
(void)local_part;
(void)pubkey_hex_out;
(void)relays;
(void)relay_count;
return NOSTR_ERROR_NETWORK_FAILED; // NIP-05 disabled at compile time
}
int nostr_nip05_lookup(const char* nip05_identifier, char* pubkey_hex_out,
char*** relays, int* relay_count, int timeout_seconds) {
(void)nip05_identifier;
(void)pubkey_hex_out;
(void)relays;
(void)relay_count;
(void)timeout_seconds;
return NOSTR_ERROR_NETWORK_FAILED; // NIP-05 disabled at compile time
}
int nostr_nip05_verify(const char* nip05_identifier, const char* pubkey_hex,
char*** relays, int* relay_count, int timeout_seconds) {
(void)nip05_identifier;
(void)pubkey_hex;
(void)relays;
(void)relay_count;
(void)timeout_seconds;
return NOSTR_ERROR_NETWORK_FAILED; // NIP-05 disabled at compile time
}
#endif // DISABLE_NIP05
}

View File

@ -4,12 +4,12 @@
#include "nip006.h"
#include "utils.h"
#include "nostr_crypto.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "../nostr_core/nostr_common.h"
int nostr_generate_keypair(unsigned char* private_key, unsigned char* public_key) {
if (!private_key || !public_key) {

View File

@ -3,11 +3,11 @@
*/
#include "nip011.h"
#include "nip005.h" // For HTTP functionality
#include "../cjson/cJSON.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../nostr_core/nostr_common.h"
#ifndef DISABLE_NIP05 // NIP-11 uses the same HTTP infrastructure as NIP-05

View File

@ -11,6 +11,7 @@
#include <string.h>
#include <time.h>
#include <stdint.h>
#include "../nostr_core/nostr_common.h"
/**
* Count leading zero bits in a hash (NIP-13 reference implementation)

View File

@ -8,6 +8,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "../nostr_core/nostr_common.h"
#define BECH32_CONST 1

490
nostr_core/nip044.c Normal file
View File

@ -0,0 +1,490 @@
/*
* NIP-44: Encrypted Payloads (Versioned) Implementation
* https://github.com/nostr-protocol/nips/blob/master/44.md
*/
#include "nip044.h"
#include "utils.h"
#include "nostr_common.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "./crypto/nostr_secp256k1.h"
// Include our ChaCha20 implementation
#include "crypto/nostr_chacha20.h"
// Forward declarations for internal functions
static size_t calc_padded_len(size_t unpadded_len);
static unsigned char* pad_plaintext(const char* plaintext, size_t* padded_len);
static char* unpad_plaintext(const unsigned char* padded, size_t padded_len);
static int constant_time_compare(const unsigned char* a, const unsigned char* b, size_t len);
// Memory clearing utility
static void memory_clear(const void *p, size_t len) {
if (p && len) {
memset((void *)p, 0, len);
}
}
// =============================================================================
// NIP-44 UTILITY FUNCTIONS
// =============================================================================
// Constant-time comparison (security critical)
static int constant_time_compare(const unsigned char* a, const unsigned char* b, size_t len) {
unsigned char result = 0;
for (size_t i = 0; i < len; i++) {
result |= (a[i] ^ b[i]);
}
return result == 0;
}
// NIP-44 padding calculation (per spec)
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);
}
// NIP-44 padding (per spec)
static unsigned char* pad_plaintext(const char* plaintext, size_t* padded_len) {
size_t unpadded_len = strlen(plaintext);
if (unpadded_len > 65535) {
return NULL;
}
// NIP-44 allows empty messages (unpadded_len can be 0)
*padded_len = calc_padded_len(unpadded_len + 2); // +2 for length prefix
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;
// 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);
return padded;
}
// NIP-44 unpadding (per spec)
static char* unpad_plaintext(const unsigned char* padded, size_t padded_len) {
if (padded_len < 2) return NULL;
// Read length prefix (big-endian u16)
size_t unpadded_len = (padded[0] << 8) | padded[1];
if (unpadded_len > padded_len - 2) {
return NULL;
}
// Verify padding length matches expected
size_t expected_padded_len = calc_padded_len(unpadded_len + 2);
if (padded_len != expected_padded_len) {
return NULL;
}
char* plaintext = malloc(unpadded_len + 1);
if (!plaintext) return NULL;
// Handle empty message case (unpadded_len can be 0)
if (unpadded_len > 0) {
memcpy(plaintext, padded + 2, unpadded_len);
}
plaintext[unpadded_len] = '\0';
return plaintext;
}
// =============================================================================
// NIP-44 IMPLEMENTATION
// =============================================================================
int nostr_nip44_encrypt_with_nonce(const unsigned char* sender_private_key,
const unsigned char* recipient_public_key,
const char* plaintext,
const unsigned char* nonce,
char* output,
size_t output_size) {
if (!sender_private_key || !recipient_public_key || !plaintext || !nonce || !output) {
return NOSTR_ERROR_INVALID_INPUT;
}
size_t plaintext_len = strlen(plaintext);
if (plaintext_len > NOSTR_NIP44_MAX_PLAINTEXT_SIZE) {
return NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL;
}
// Step 1: Compute ECDH shared secret
unsigned char shared_secret[32];
if (ecdh_shared_secret(sender_private_key, recipient_public_key, shared_secret) != 0) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 2: Calculate conversation key (HKDF-extract with "nip44-v2" as salt)
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) {
memory_clear(shared_secret, 32);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 3: Use provided nonce (for testing)
// Copy nonce for consistency with existing code structure
unsigned char nonce_copy[32];
memcpy(nonce_copy, nonce, 32);
// Step 4: Derive message keys (HKDF-expand with nonce as info)
unsigned char message_keys[76]; // 32 chacha_key + 12 chacha_nonce + 32 hmac_key
if (nostr_hkdf_expand(conversation_key, 32, nonce_copy, 32, message_keys, 76) != 0) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(nonce_copy, 32);
return NOSTR_ERROR_CRYPTO_FAILED;
}
unsigned char* chacha_key = message_keys;
unsigned char* chacha_nonce = message_keys + 32;
unsigned char* hmac_key = message_keys + 44;
// Step 5: Pad plaintext according to NIP-44 spec
size_t padded_len;
unsigned char* padded_plaintext = pad_plaintext(plaintext, &padded_len);
if (!padded_plaintext) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(nonce, 32);
memory_clear(message_keys, 76);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 6: Encrypt using ChaCha20
unsigned char* ciphertext = malloc(padded_len);
if (!ciphertext) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(nonce, 32);
memory_clear(message_keys, 76);
memory_clear(padded_plaintext, padded_len);
free(padded_plaintext);
return NOSTR_ERROR_MEMORY_FAILED;
}
if (chacha20_encrypt(chacha_key, 0, chacha_nonce, padded_plaintext, ciphertext, padded_len) != 0) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(nonce, 32);
memory_clear(message_keys, 76);
memory_clear(padded_plaintext, padded_len);
free(padded_plaintext);
free(ciphertext);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 7: Compute HMAC with AAD (nonce + ciphertext)
unsigned char* aad_data = malloc(32 + padded_len);
if (!aad_data) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(nonce_copy, 32);
memory_clear(message_keys, 76);
memory_clear(padded_plaintext, padded_len);
free(padded_plaintext);
free(ciphertext);
return NOSTR_ERROR_MEMORY_FAILED;
}
memcpy(aad_data, nonce_copy, 32);
memcpy(aad_data + 32, ciphertext, padded_len);
unsigned char mac[32];
if (nostr_hmac_sha256(hmac_key, 32, aad_data, 32 + padded_len, mac) != 0) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(nonce, 32);
memory_clear(message_keys, 76);
memory_clear(padded_plaintext, padded_len);
memory_clear(aad_data, 32 + padded_len);
free(padded_plaintext);
free(ciphertext);
free(aad_data);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 8: Format as base64(version + nonce + ciphertext + mac)
size_t payload_len = 1 + 32 + padded_len + 32; // version + nonce + ciphertext + mac
unsigned char* payload = malloc(payload_len);
if (!payload) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(nonce, 32);
memory_clear(message_keys, 76);
memory_clear(padded_plaintext, padded_len);
memory_clear(aad_data, 32 + padded_len);
free(padded_plaintext);
free(ciphertext);
free(aad_data);
return NOSTR_ERROR_MEMORY_FAILED;
}
payload[0] = 0x02; // NIP-44 version 2
memcpy(payload + 1, nonce_copy, 32);
memcpy(payload + 33, ciphertext, padded_len);
memcpy(payload + 33 + padded_len, mac, 32);
// Base64 encode
size_t b64_len = ((payload_len + 2) / 3) * 4 + 1;
if (b64_len > output_size) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(nonce, 32);
memory_clear(message_keys, 76);
memory_clear(padded_plaintext, padded_len);
memory_clear(aad_data, 32 + padded_len);
memory_clear(payload, payload_len);
free(padded_plaintext);
free(ciphertext);
free(aad_data);
free(payload);
return NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL;
}
if (base64_encode(payload, payload_len, output, output_size) == 0) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(nonce, 32);
memory_clear(message_keys, 76);
memory_clear(padded_plaintext, padded_len);
memory_clear(aad_data, 32 + padded_len);
memory_clear(payload, payload_len);
free(padded_plaintext);
free(ciphertext);
free(aad_data);
free(payload);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Cleanup
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(nonce_copy, 32);
memory_clear(message_keys, 76);
memory_clear(padded_plaintext, padded_len);
memory_clear(aad_data, 32 + padded_len);
memory_clear(payload, payload_len);
free(padded_plaintext);
free(ciphertext);
free(aad_data);
free(payload);
return NOSTR_SUCCESS;
}
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) {
// Generate random nonce and call the _with_nonce version
unsigned char nonce[32];
if (nostr_secp256k1_get_random_bytes(nonce, 32) != 1) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
return nostr_nip44_encrypt_with_nonce(sender_private_key, recipient_public_key,
plaintext, nonce, output, 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) {
if (!recipient_private_key || !sender_public_key || !encrypted_data || !output) {
return NOSTR_ERROR_INVALID_INPUT;
}
// Step 1: Base64 decode the encrypted data
size_t max_payload_len = ((strlen(encrypted_data) + 3) / 4) * 3;
unsigned char* payload = malloc(max_payload_len);
if (!payload) {
return NOSTR_ERROR_MEMORY_FAILED;
}
size_t payload_len = base64_decode(encrypted_data, payload);
if (payload_len < 66) { // Minimum: version(1) + nonce(32) + mac(32) + 1 byte ciphertext
free(payload);
return NOSTR_ERROR_NIP44_INVALID_FORMAT;
}
// Step 2: Extract components (version + nonce + ciphertext + mac)
if (payload[0] != 0x02) { // Check NIP-44 version
free(payload);
return NOSTR_ERROR_NIP44_INVALID_FORMAT;
}
unsigned char* nonce = payload + 1;
size_t ciphertext_len = payload_len - 65; // payload - version - nonce - mac
unsigned char* ciphertext = payload + 33;
unsigned char* received_mac = payload + payload_len - 32;
// Step 3: Compute ECDH shared secret
unsigned char shared_secret[32];
if (ecdh_shared_secret(recipient_private_key, sender_public_key, shared_secret) != 0) {
memory_clear(payload, payload_len);
free(payload);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 4: Calculate conversation key (HKDF-extract with "nip44-v2" as salt)
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) {
memory_clear(shared_secret, 32);
memory_clear(payload, payload_len);
free(payload);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 5: Derive message keys (HKDF-expand with nonce as info)
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) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(payload, payload_len);
free(payload);
return NOSTR_ERROR_CRYPTO_FAILED;
}
unsigned char* chacha_key = message_keys;
unsigned char* chacha_nonce = message_keys + 32;
unsigned char* hmac_key = message_keys + 44;
// Step 6: Verify HMAC with AAD (nonce + ciphertext)
unsigned char* aad_data = malloc(32 + ciphertext_len);
if (!aad_data) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(message_keys, 76);
memory_clear(payload, payload_len);
free(payload);
return NOSTR_ERROR_MEMORY_FAILED;
}
memcpy(aad_data, nonce, 32);
memcpy(aad_data + 32, ciphertext, ciphertext_len);
unsigned char computed_mac[32];
if (nostr_hmac_sha256(hmac_key, 32, aad_data, 32 + ciphertext_len, computed_mac) != 0) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(message_keys, 76);
memory_clear(aad_data, 32 + ciphertext_len);
memory_clear(payload, payload_len);
free(aad_data);
free(payload);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Constant-time MAC verification
if (!constant_time_compare(received_mac, computed_mac, 32)) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(message_keys, 76);
memory_clear(aad_data, 32 + ciphertext_len);
memory_clear(payload, payload_len);
free(aad_data);
free(payload);
return NOSTR_ERROR_NIP44_DECRYPT_FAILED;
}
// Step 7: Decrypt using ChaCha20
unsigned char* padded_plaintext = malloc(ciphertext_len);
if (!padded_plaintext) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(message_keys, 76);
memory_clear(aad_data, 32 + ciphertext_len);
memory_clear(payload, payload_len);
free(aad_data);
free(payload);
return NOSTR_ERROR_MEMORY_FAILED;
}
if (chacha20_encrypt(chacha_key, 0, chacha_nonce, ciphertext, padded_plaintext, ciphertext_len) != 0) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(message_keys, 76);
memory_clear(aad_data, 32 + ciphertext_len);
memory_clear(payload, payload_len);
free(aad_data);
free(payload);
free(padded_plaintext);
return NOSTR_ERROR_CRYPTO_FAILED;
}
// Step 8: Remove padding according to NIP-44 spec
char* plaintext = unpad_plaintext(padded_plaintext, ciphertext_len);
if (!plaintext) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(message_keys, 76);
memory_clear(aad_data, 32 + ciphertext_len);
memory_clear(payload, payload_len);
memory_clear(padded_plaintext, ciphertext_len);
free(aad_data);
free(payload);
free(padded_plaintext);
return NOSTR_ERROR_NIP44_DECRYPT_FAILED;
}
// Step 9: Copy to output buffer
size_t plaintext_len = strlen(plaintext);
if (plaintext_len + 1 > output_size) {
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(message_keys, 76);
memory_clear(aad_data, 32 + ciphertext_len);
memory_clear(payload, payload_len);
memory_clear(padded_plaintext, ciphertext_len);
memory_clear(plaintext, plaintext_len);
free(aad_data);
free(payload);
free(padded_plaintext);
free(plaintext);
return NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL;
}
strcpy(output, plaintext);
// Cleanup
memory_clear(shared_secret, 32);
memory_clear(conversation_key, 32);
memory_clear(message_keys, 76);
memory_clear(aad_data, 32 + ciphertext_len);
memory_clear(payload, payload_len);
memory_clear(padded_plaintext, ciphertext_len);
memory_clear(plaintext, plaintext_len);
free(aad_data);
free(payload);
free(padded_plaintext);
free(plaintext);
return NOSTR_SUCCESS;
}

72
nostr_core/nip044.h Normal file
View File

@ -0,0 +1,72 @@
/*
* NIP-44: Encrypted Payloads (Versioned)
* https://github.com/nostr-protocol/nips/blob/master/44.md
*/
#ifndef NOSTR_NIP044_H
#define NOSTR_NIP044_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// NIP-44 constants
// #define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 65535
/**
* NIP-44: Encrypt a message using ECDH + ChaCha20 + HMAC
*
* @param sender_private_key 32-byte sender private key
* @param recipient_public_key 32-byte recipient public key (x-only)
* @param plaintext Message to encrypt
* @param output Buffer for encrypted output (base64 encoded)
* @param output_size Size of output buffer
* @return NOSTR_SUCCESS on success, error code on failure
*/
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);
/**
* NIP-44: Encrypt a message with a specific nonce (for testing)
*
* @param sender_private_key 32-byte sender private key
* @param recipient_public_key 32-byte recipient public key (x-only)
* @param plaintext Message to encrypt
* @param nonce 32-byte nonce
* @param output Buffer for encrypted output (base64 encoded)
* @param output_size Size of output buffer
* @return NOSTR_SUCCESS on success, error code on failure
*/
int nostr_nip44_encrypt_with_nonce(const unsigned char* sender_private_key,
const unsigned char* recipient_public_key,
const char* plaintext,
const unsigned char* nonce,
char* output,
size_t output_size);
/**
* NIP-44: Decrypt a message using ECDH + ChaCha20 + HMAC
*
* @param recipient_private_key 32-byte recipient private key
* @param sender_public_key 32-byte sender public key (x-only)
* @param encrypted_data Encrypted message (base64 encoded)
* @param output Buffer for decrypted plaintext
* @param output_size Size of output buffer
* @return NOSTR_SUCCESS on success, error code on failure
*/
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);
#ifdef __cplusplus
}
#endif
#endif // NOSTR_NIP044_H

52
nostr_core/nostr_common.c Normal file
View File

@ -0,0 +1,52 @@
/*
* NOSTR Core Library - Common Utilities
*
* Common functions and utilities shared across the library
*/
#include "nostr_common.h"
#include "utils.h"
/**
* Convert error code to human-readable string
* Handles all error codes defined in nostr_common.h
*/
const char* nostr_strerror(int error_code) {
switch (error_code) {
case NOSTR_SUCCESS: return "Success";
case NOSTR_ERROR_INVALID_INPUT: return "Invalid input";
case NOSTR_ERROR_CRYPTO_FAILED: return "Cryptographic operation failed";
case NOSTR_ERROR_MEMORY_FAILED: return "Memory allocation failed";
case NOSTR_ERROR_IO_FAILED: return "I/O operation failed";
case NOSTR_ERROR_NETWORK_FAILED: return "Network operation failed";
case NOSTR_ERROR_NIP04_INVALID_FORMAT: return "NIP-04 invalid format";
case NOSTR_ERROR_NIP04_DECRYPT_FAILED: return "NIP-04 decryption failed";
case NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL: return "NIP-04 buffer too small";
case NOSTR_ERROR_NIP44_INVALID_FORMAT: return "NIP-44: Invalid format";
case NOSTR_ERROR_NIP44_DECRYPT_FAILED: return "NIP-44: Decryption failed";
case NOSTR_ERROR_NIP44_BUFFER_TOO_SMALL: return "NIP-44: Buffer too small";
case NOSTR_ERROR_NIP05_INVALID_IDENTIFIER: return "NIP-05: Invalid identifier format";
case NOSTR_ERROR_NIP05_HTTP_FAILED: return "NIP-05: HTTP request failed";
case NOSTR_ERROR_NIP05_JSON_PARSE_FAILED: return "NIP-05: JSON parsing failed";
case NOSTR_ERROR_NIP05_NAME_NOT_FOUND: return "NIP-05: Name not found in .well-known";
case NOSTR_ERROR_NIP05_PUBKEY_MISMATCH: return "NIP-05: Public key mismatch";
default: return "Unknown error";
}
}
/**
* Initialize the NOSTR library
*/
int nostr_init(void) {
if (nostr_crypto_init() != 0) {
return NOSTR_ERROR_CRYPTO_FAILED;
}
return NOSTR_SUCCESS;
}
/**
* Cleanup the NOSTR library
*/
void nostr_cleanup(void) {
nostr_crypto_cleanup();
}

View File

@ -28,6 +28,7 @@
#define NOSTR_ERROR_NIP05_NAME_NOT_FOUND -19
#define NOSTR_ERROR_NIP05_PUBKEY_MISMATCH -20
// Constants
#define NOSTR_PRIVATE_KEY_SIZE 32
#define NOSTR_PUBLIC_KEY_SIZE 32
@ -35,6 +36,7 @@
#define NOSTR_BECH32_KEY_SIZE 100
#define NOSTR_MAX_CONTENT_SIZE 2048
#define NOSTR_MAX_URL_SIZE 256
#define NIP05_DEFAULT_TIMEOUT 10
// NIP-04 Constants
#define NOSTR_NIP04_MAX_PLAINTEXT_SIZE 16777216 // 16MB
@ -43,4 +45,11 @@
// NIP-44 Constants
#define NOSTR_NIP44_MAX_PLAINTEXT_SIZE 65536 // 64KB max plaintext (matches crypto header)
// Function declarations
const char* nostr_strerror(int error_code);
// Library initialization functions
int nostr_init(void);
void nostr_cleanup(void);
#endif // NOSTR_COMMON_H

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,165 @@
/*
* NOSTR Core Library - Utilities
*
*
* General utility functions used across multiple NIPs
*/
#ifndef UTILS_H
#define UTILS_H
#ifndef NOSTR_UTILS_H
#define NOSTR_UTILS_H
#include <stddef.h>
#include <stdint.h>
// Error codes (imported from nip001.h)
#define NOSTR_SUCCESS 0
#define NOSTR_ERROR_INVALID_INPUT -1
#ifdef __cplusplus
extern "C" {
#endif
// Utility function declarations
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);
// =============================================================================
// UTILITY FUNCTIONS
// =============================================================================
#endif // UTILS_H
// Convert bytes to hexadecimal string
void nostr_bytes_to_hex(const unsigned char *bytes, size_t len, char *hex);
// Convert hexadecimal string to bytes
int nostr_hex_to_bytes(const char *hex, unsigned char *bytes, size_t len);
// Base64 encoding function
size_t base64_encode(const unsigned char *data, size_t len, char *output,
size_t output_size);
// Base64 decoding function
size_t base64_decode(const char *input, unsigned char *output);
// =============================================================================
// CORE CRYPTO FUNCTIONS
// =============================================================================
// Initialize crypto subsystem
int nostr_crypto_init(void);
// Cleanup crypto subsystem
void nostr_crypto_cleanup(void);
// SHA-256 hash function
int nostr_sha256(const unsigned char *data, size_t len, unsigned char *hash);
// HMAC-SHA256
int nostr_hmac_sha256(const unsigned char *key, size_t key_len,
const unsigned char *data, size_t data_len,
unsigned char *output);
// HMAC-SHA512
int nostr_hmac_sha512(const unsigned char *key, size_t key_len,
const unsigned char *data, size_t data_len,
unsigned char *output);
// PBKDF2 with HMAC-SHA512
int nostr_pbkdf2_hmac_sha512(const unsigned char *password, size_t password_len,
const unsigned char *salt, size_t salt_len,
int iterations, unsigned char *output,
size_t output_len);
// SHA-512 implementation (for testing)
int nostr_sha512(const unsigned char *data, size_t len, unsigned char *hash);
// =============================================================================
// SECP256K1 ELLIPTIC CURVE FUNCTIONS
// =============================================================================
// Verify private key is valid
int nostr_ec_private_key_verify(const unsigned char *private_key);
// Generate public key from private key
int nostr_ec_public_key_from_private_key(const unsigned char *private_key,
unsigned char *public_key);
// Sign data with ECDSA
int nostr_ec_sign(const unsigned char *private_key, const unsigned char *hash,
unsigned char *signature);
// RFC 6979 deterministic nonce generation
int nostr_rfc6979_generate_k(const unsigned char *private_key,
const unsigned char *message_hash,
unsigned char *k_out);
int nostr_schnorr_sign(const unsigned char* private_key,
const unsigned char* hash,
unsigned char* signature);
// =============================================================================
// HKDF KEY DERIVATION FUNCTIONS
// =============================================================================
// HKDF Extract step
int nostr_hkdf_extract(const unsigned char *salt, size_t salt_len,
const unsigned char *ikm, size_t ikm_len,
unsigned char *prk);
// HKDF Expand step
int nostr_hkdf_expand(const unsigned char *prk, size_t prk_len,
const unsigned char *info, size_t info_len,
unsigned char *okm, size_t okm_len);
// HKDF (Extract + Expand)
int nostr_hkdf(const unsigned char *salt, size_t salt_len,
const unsigned char *ikm, size_t ikm_len,
const unsigned char *info, size_t info_len, unsigned char *okm,
size_t okm_len);
// ECDH shared secret computation (for debugging)
int ecdh_shared_secret(const unsigned char *private_key,
const unsigned char *public_key_x,
unsigned char *shared_secret);
// =============================================================================
// BIP39 MNEMONIC FUNCTIONS
// =============================================================================
// Generate mnemonic from entropy
int nostr_bip39_mnemonic_from_bytes(const unsigned char *entropy,
size_t entropy_len, char *mnemonic);
// Validate mnemonic
int nostr_bip39_mnemonic_validate(const char *mnemonic);
// Convert mnemonic to seed
int nostr_bip39_mnemonic_to_seed(const char *mnemonic, const char *passphrase,
unsigned char *seed, size_t seed_len);
// =============================================================================
// BIP32 HD WALLET FUNCTIONS
// =============================================================================
typedef struct {
unsigned char private_key[32];
unsigned char public_key[33];
unsigned char chain_code[32];
uint32_t depth;
uint32_t parent_fingerprint;
uint32_t child_number;
} nostr_hd_key_t;
// Create master key from seed
int nostr_bip32_key_from_seed(const unsigned char *seed, size_t seed_len,
nostr_hd_key_t *master_key);
// Derive child key from parent
int nostr_bip32_derive_child(const nostr_hd_key_t *parent_key,
uint32_t child_number, nostr_hd_key_t *child_key);
// Derive key from path
int nostr_bip32_derive_path(const nostr_hd_key_t *master_key,
const uint32_t *path, size_t path_len,
nostr_hd_key_t *derived_key);
#ifdef __cplusplus
}
#endif
#endif // NOSTR_UTILS_H

Binary file not shown.

View File

@ -9,8 +9,9 @@
#include <curl/curl.h>
// Callback to write received data
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, char *userp) {
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t realsize = size * nmemb;
(void)userp; // Mark parameter as deliberately unused
printf("%.*s", (int)realsize, (char*)contents);
return realsize;
}
@ -30,7 +31,7 @@ int main() {
curl_easy_setopt(curl, CURLOPT_URL, "https://google.com");
// Set callback for received data
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)WriteCallback);
// Follow redirects
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);

BIN
tests/nip04_test Executable file

Binary file not shown.

View File

@ -6,7 +6,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../nostr_core/nostr_core.h"
#include "../nostr_core/nip004.h"
#include "../nostr_core/nostr_common.h"
void print_hex(const char* label, const unsigned char* data, size_t len) {
printf("%s: ", label);
@ -756,10 +758,10 @@ int main(void) {
printf("=== NIP-04 Encryption Test with Reference Test Vectors ===\n\n");
// Initialize the library
if (nostr_init() != NOSTR_SUCCESS) {
printf("ERROR: Failed to initialize NOSTR library\n");
return 1;
}
// if (nostr_init() != NOSTR_SUCCESS) {
// printf("ERROR: Failed to initialize NOSTR library\n");
// return 1;
// }
int all_passed = 1;
@ -811,6 +813,6 @@ int main(void) {
printf("❌ SOME TESTS FAILED. Please review the output above.\n");
}
nostr_cleanup();
// nostr_cleanup();
return all_passed ? 0 : 1;
}

Binary file not shown.

View File

@ -9,6 +9,7 @@
#include <string.h>
#include <unistd.h>
#include "../nostr_core/nip005.h"
#include "../nostr_core/nostr_common.h"
// Test helper function
void print_test_result(const char* test_name, int result) {

BIN
tests/nip11_test Executable file

Binary file not shown.

View File

@ -7,8 +7,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../nostr_core/nostr_core.h"
#include "../cjson/cJSON.h"
#include "../nostr_core/nip011.h"
#include "../nostr_core/nostr_common.h"
// Test counter
static int tests_run = 0;

Binary file not shown.

View File

@ -1,348 +0,0 @@
/*
* Makefile-Based Static Linking Test
*
* This test validates static linking configuration by parsing the Makefile
* instead of analyzing compiled binaries. This approach is faster, more reliable,
* and catches configuration issues at the source.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// Test result tracking
static int tests_run = 0;
static int tests_passed = 0;
// Test macros
#define ASSERT(condition, message) \
do { \
tests_run++; \
if (condition) { \
printf("✓ %s: PASSED\n", message); \
tests_passed++; \
} else { \
printf("✗ %s: FAILED\n", message); \
} \
} while(0)
#define ASSERT_CONTAINS(haystack, needle, message) \
ASSERT(strstr(haystack, needle) != NULL, message)
#define ASSERT_NOT_CONTAINS(haystack, needle, message) \
ASSERT(strstr(haystack, needle) == NULL, message)
// File reading utility
char* read_file(const char* filename) {
FILE* file = fopen(filename, "r");
if (!file) {
printf("ERROR: Cannot open %s\n", filename);
return NULL;
}
fseek(file, 0, SEEK_END);
long length = ftell(file);
fseek(file, 0, SEEK_SET);
char* content = malloc(length + 1);
if (!content) {
fclose(file);
return NULL;
}
fread(content, 1, length, file);
content[length] = '\0';
fclose(file);
return content;
}
// Extract variable value from Makefile
char* extract_makefile_variable(const char* content, const char* variable) {
char search_pattern[256];
snprintf(search_pattern, sizeof(search_pattern), "%s =", variable);
char* line_start = strstr(content, search_pattern);
if (!line_start) {
snprintf(search_pattern, sizeof(search_pattern), "%s=", variable);
line_start = strstr(content, search_pattern);
}
if (!line_start) return NULL;
// Find start of value (after '=')
char* value_start = strchr(line_start, '=');
if (!value_start) return NULL;
value_start++; // Skip '='
// Skip whitespace
while (*value_start == ' ' || *value_start == '\t') {
value_start++;
}
// Find end of line
char* line_end = strchr(value_start, '\n');
if (!line_end) {
line_end = value_start + strlen(value_start);
}
// Handle line continuations with backslash
char* result = malloc(2048);
char* result_ptr = result;
char* current = value_start;
while (current < line_end) {
if (*current == '\\' && (current + 1) < line_end && *(current + 1) == '\n') {
// Line continuation - skip to next line
current += 2; // Skip '\\\n'
// Find next non-whitespace
while (current < line_end && (*current == ' ' || *current == '\t')) {
current++;
}
*result_ptr++ = ' '; // Add space between continued lines
} else {
*result_ptr++ = *current++;
}
}
*result_ptr = '\0';
// Trim trailing whitespace
result_ptr--;
while (result_ptr > result && (*result_ptr == ' ' || *result_ptr == '\t')) {
*result_ptr-- = '\0';
}
return result;
}
// Test static linking configuration
void test_static_linking_flags(const char* makefile_content) {
printf("\n=== Static Linking Flags Test ===\n");
// Check TEST_LDFLAGS contains -static
char* test_ldflags = extract_makefile_variable(makefile_content, "TEST_LDFLAGS");
if (test_ldflags) {
ASSERT_CONTAINS(test_ldflags, "-static", "TEST_LDFLAGS contains -static flag");
free(test_ldflags);
} else {
ASSERT(0, "TEST_LDFLAGS variable found in Makefile");
}
// Check ARM64_TEST_LDFLAGS contains -static
char* arm64_test_ldflags = extract_makefile_variable(makefile_content, "ARM64_TEST_LDFLAGS");
if (arm64_test_ldflags) {
ASSERT_CONTAINS(arm64_test_ldflags, "-static", "ARM64_TEST_LDFLAGS contains -static flag");
free(arm64_test_ldflags);
} else {
ASSERT(0, "ARM64_TEST_LDFLAGS variable found in Makefile");
}
}
// Test forbidden dynamic library links
void test_forbidden_dynamic_links(const char* makefile_content) {
printf("\n=== Forbidden Dynamic Links Test ===\n");
// List of libraries that should not be dynamically linked in core tests
const char* forbidden_libs[] = {
"-lsecp256k1", // Should be statically included
"-lsodium", // Not used
"-lwally", // Not used
"-lmbedtls", // Replaced with OpenSSL
"-lmbedx509", // Replaced with OpenSSL
"-lmbedcrypto", // Replaced with OpenSSL
NULL
};
for (int i = 0; forbidden_libs[i] != NULL; i++) {
// Check that forbidden library is not in TEST_LDFLAGS
char* test_ldflags = extract_makefile_variable(makefile_content, "TEST_LDFLAGS");
if (test_ldflags) {
char message[256];
snprintf(message, sizeof(message), "TEST_LDFLAGS does not contain %s", forbidden_libs[i]);
ASSERT_NOT_CONTAINS(test_ldflags, forbidden_libs[i], message);
free(test_ldflags);
}
}
}
// Test that we use the static library
void test_static_library_usage(const char* makefile_content) {
printf("\n=== Static Library Usage Test ===\n");
// Check that TEST_LDFLAGS links against our static library
char* test_ldflags = extract_makefile_variable(makefile_content, "TEST_LDFLAGS");
if (test_ldflags) {
ASSERT_CONTAINS(test_ldflags, "-lnostr_core", "TEST_LDFLAGS links against libnostr_core");
ASSERT_CONTAINS(test_ldflags, "-lm", "TEST_LDFLAGS links against math library");
free(test_ldflags);
}
// Check ARM64 version
char* arm64_test_ldflags = extract_makefile_variable(makefile_content, "ARM64_TEST_LDFLAGS");
if (arm64_test_ldflags) {
ASSERT_CONTAINS(arm64_test_ldflags, "-lnostr_core_arm64", "ARM64_TEST_LDFLAGS links against ARM64 static library");
free(arm64_test_ldflags);
}
}
// Test that compilation flags disable problematic features for static tests only
void test_compilation_flags(const char* makefile_content) {
printf("\n=== Compilation Flags Test ===\n");
// Check TEST_CFLAGS contains DISABLE_NIP05 (static tests need to avoid curl)
char* test_cflags = extract_makefile_variable(makefile_content, "TEST_CFLAGS");
if (test_cflags) {
ASSERT_CONTAINS(test_cflags, "-DDISABLE_NIP05", "TEST_CFLAGS disables NIP-05 to avoid curl dependency");
free(test_cflags);
}
// Check that main compilation does NOT disable NIP05 (NIP-05 should be enabled by default)
// Look for DISABLE_NIP05 in object file compilation rules - should NOT be there
char* obj_rule_start = strstr(makefile_content, "%.o: %.c");
if (obj_rule_start) {
char* obj_rule_end = strstr(obj_rule_start, "\n\n");
if (!obj_rule_end) obj_rule_end = makefile_content + strlen(makefile_content);
char* obj_rule = malloc(obj_rule_end - obj_rule_start + 1);
strncpy(obj_rule, obj_rule_start, obj_rule_end - obj_rule_start);
obj_rule[obj_rule_end - obj_rule_start] = '\0';
ASSERT_NOT_CONTAINS(obj_rule, "-DDISABLE_NIP05", "Main compilation does not disable NIP-05");
free(obj_rule);
}
}
// Test static curl usage (no dynamic -lcurl)
void test_static_curl_usage(const char* makefile_content) {
printf("\n=== Static Curl Usage Test ===\n");
// Count occurrences of -lcurl (should be zero - we use static libcurl.a)
int curl_count = 0;
const char* search_pos = makefile_content;
while ((search_pos = strstr(search_pos, "-lcurl")) != NULL) {
curl_count++;
search_pos += 6; // Move past "-lcurl"
}
char message[256];
snprintf(message, sizeof(message), "No dynamic curl usage found (found %d -lcurl occurrences)", curl_count);
ASSERT(curl_count == 0, message);
// Verify HTTP and NIP-05 tests use static libcurl.a instead
ASSERT_CONTAINS(makefile_content, "./curl-install/lib/libcurl.a", "Static libcurl.a is used");
// Verify curl include path is used
ASSERT_CONTAINS(makefile_content, "-I./curl-install/include", "Curl include path is used");
// Verify both HTTP and NIP-05 tests use static linking
char* http_test_line = strstr(makefile_content, "$(HTTP_TEST_EXEC): tests/http_test.c");
if (http_test_line) {
char* next_rule = strstr(http_test_line, "\n\n");
if (!next_rule) next_rule = makefile_content + strlen(makefile_content);
char* http_section = malloc(next_rule - http_test_line + 1);
strncpy(http_section, http_test_line, next_rule - http_test_line);
http_section[next_rule - http_test_line] = '\0';
ASSERT_CONTAINS(http_section, "./curl-install/lib/libcurl.a", "HTTP test uses static libcurl.a");
ASSERT_CONTAINS(http_section, "-static", "HTTP test uses static linking");
free(http_section);
}
char* nip05_test_line = strstr(makefile_content, "$(NIP05_TEST_EXEC): tests/nip05_test.c");
if (nip05_test_line) {
char* next_rule = strstr(nip05_test_line, "\n\n");
if (!next_rule) next_rule = makefile_content + strlen(makefile_content);
char* nip05_section = malloc(next_rule - nip05_test_line + 1);
strncpy(nip05_section, nip05_test_line, next_rule - nip05_test_line);
nip05_section[next_rule - nip05_test_line] = '\0';
ASSERT_CONTAINS(nip05_section, "./curl-install/lib/libcurl.a", "NIP-05 test uses static libcurl.a");
ASSERT_CONTAINS(nip05_section, "-static", "NIP-05 test uses static linking");
free(nip05_section);
}
}
// Test that only one Makefile exists
void test_single_makefile_policy() {
printf("\n=== Single Makefile Policy Test ===\n");
// Check that subdirectory Makefiles don't exist or are minimal/deprecated
int makefile_violations = 0;
// Check tests/Makefile
if (access("tests/Makefile", F_OK) == 0) {
char* tests_makefile = read_file("tests/Makefile");
if (tests_makefile) {
// If tests/Makefile exists and contains actual build rules, it's a violation
if (strstr(tests_makefile, "LDFLAGS") || strstr(tests_makefile, "gcc")) {
makefile_violations++;
printf("WARNING: tests/Makefile contains build rules (should be consolidated)\n");
}
free(tests_makefile);
}
}
// Check nostr_websocket/Makefile
if (access("nostr_websocket/Makefile", F_OK) == 0) {
char* websocket_makefile = read_file("nostr_websocket/Makefile");
if (websocket_makefile) {
// If websocket Makefile exists and contains build rules, it's a violation
if (strstr(websocket_makefile, "LDFLAGS") || strstr(websocket_makefile, "gcc")) {
makefile_violations++;
printf("WARNING: nostr_websocket/Makefile contains build rules (should be consolidated)\n");
}
free(websocket_makefile);
}
}
char message[256];
snprintf(message, sizeof(message), "No Makefile policy violations found (found %d violations)", makefile_violations);
ASSERT(makefile_violations == 0, message);
}
int main() {
printf("Makefile-Based Static Linking Test\n");
printf("==================================\n");
printf("Testing static linking configuration by parsing Makefile...\n");
// Read the main Makefile
char* makefile_content = read_file("Makefile");
if (!makefile_content) {
printf("FATAL: Cannot read Makefile\n");
return 1;
}
// Run all tests
test_static_linking_flags(makefile_content);
test_forbidden_dynamic_links(makefile_content);
test_static_library_usage(makefile_content);
test_compilation_flags(makefile_content);
test_static_curl_usage(makefile_content);
test_single_makefile_policy();
free(makefile_content);
// Summary
printf("\n============================================\n");
printf("TEST SUMMARY\n");
printf("============================================\n");
printf("Tests run: %d\n", tests_run);
printf("Tests passed: %d\n", tests_passed);
if (tests_passed == tests_run) {
printf("ALL TESTS PASSED!\n");
printf("✅ Makefile static linking configuration is correct\n");
printf("✅ No forbidden dynamic dependencies\n");
printf("✅ Single Makefile policy enforced\n");
return 0;
} else {
printf("%d TESTS FAILED!\n", tests_run - tests_passed);
printf("❌ Makefile configuration needs fixes\n");
return 1;
}
}

View File

@ -8,7 +8,7 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "../nostr_core/nostr_crypto.h"
#include "../nostr_core/utils.h"
// Helper function to convert hex string to bytes
static void hex_to_bytes(const char* hex, unsigned char* bytes, size_t len) {

Binary file not shown.

View File

@ -1,5 +1,5 @@
#include <stdio.h>
#include "../nostr_core/nip001.h"
#include "../nostr_core/nostr_common.h"
int main(void) {
printf("Testing basic library initialization...\n");