Completed refactoring to separate nip files, and updating build.sh
This commit is contained in:
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
336
nostr_core/nip004.c
Normal 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
56
nostr_core/nip004.h
Normal 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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
490
nostr_core/nip044.c
Normal 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
72
nostr_core/nip044.h
Normal 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
52
nostr_core/nostr_common.c
Normal 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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
1374
nostr_core/utils.c
1374
nostr_core/utils.c
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user