Fixed bug in nip44.c, was an error in ecdh_shared_secret. Added comments

This commit is contained in:
2025-08-17 11:29:07 -04:00
parent d8b342ca3f
commit 3ebfdc06c0
14 changed files with 15 additions and 69 deletions
-34
View File
@@ -107,18 +107,13 @@ int nostr_nip04_encrypt(const unsigned char* sender_private_key,
const char* plaintext,
char* output,
size_t output_size) {
printf("[DEBUG] nostr_nip04_encrypt: Starting encryption\n");
if (!sender_private_key || !recipient_public_key || !plaintext || !output) {
printf("[DEBUG] nostr_nip04_encrypt: Invalid input - null pointer detected\n");
return NOSTR_ERROR_INVALID_INPUT;
}
size_t plaintext_len = strlen(plaintext);
printf("[DEBUG] nostr_nip04_encrypt: Plaintext length = %zu\n", plaintext_len);
if (plaintext_len > NOSTR_NIP04_MAX_PLAINTEXT_SIZE) {
printf("[DEBUG] nostr_nip04_encrypt: Plaintext too large (%zu > %d)\n", plaintext_len, NOSTR_NIP04_MAX_PLAINTEXT_SIZE);
return NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL;
}
@@ -130,64 +125,46 @@ int nostr_nip04_encrypt(const unsigned char* sender_private_key,
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="
printf("[DEBUG] nostr_nip04_encrypt: Size calculations - padded_len=%zu, ciphertext_b64_max=%zu, estimated_result_len=%zu, output_size=%zu\n",
padded_len, ciphertext_b64_max, estimated_result_len, output_size);
// FIX: Check output buffer size BEFORE doing any work
if (estimated_result_len > output_size) {
printf("[DEBUG] nostr_nip04_encrypt: Output buffer too small (%zu > %zu)\n", estimated_result_len, output_size);
return NOSTR_ERROR_NIP04_BUFFER_TOO_SMALL;
}
// Step 1: Compute ECDH shared secret
printf("[DEBUG] nostr_nip04_encrypt: Computing ECDH shared secret\n");
unsigned char shared_secret[32];
if (ecdh_shared_secret(sender_private_key, recipient_public_key, shared_secret) != 0) {
printf("[DEBUG] nostr_nip04_encrypt: ECDH shared secret computation failed\n");
return NOSTR_ERROR_CRYPTO_FAILED;
}
printf("[DEBUG] nostr_nip04_encrypt: ECDH shared secret computed successfully\n");
// Step 2: Generate random IV (16 bytes)
printf("[DEBUG] nostr_nip04_encrypt: Generating random IV\n");
unsigned char iv[16];
if (nostr_secp256k1_get_random_bytes(iv, 16) != 1) {
printf("[DEBUG] nostr_nip04_encrypt: Failed to generate random IV\n");
return NOSTR_ERROR_CRYPTO_FAILED;
}
printf("[DEBUG] nostr_nip04_encrypt: IV generated successfully\n");
// Step 3: Pad plaintext using PKCS#7
printf("[DEBUG] nostr_nip04_encrypt: Padding plaintext with PKCS#7\n");
unsigned char* padded_data = malloc(padded_len);
if (!padded_data) {
printf("[DEBUG] nostr_nip04_encrypt: Failed to allocate memory for padded data\n");
return NOSTR_ERROR_MEMORY_FAILED;
}
memcpy(padded_data, plaintext, plaintext_len);
size_t actual_padded_len = pkcs7_pad(padded_data, plaintext_len, 16);
printf("[DEBUG] nostr_nip04_encrypt: PKCS#7 padding completed - actual_padded_len=%zu\n", actual_padded_len);
// Step 4: Encrypt using AES-256-CBC
printf("[DEBUG] nostr_nip04_encrypt: Encrypting with AES-256-CBC\n");
unsigned char* ciphertext = malloc(padded_len);
if (!ciphertext) {
printf("[DEBUG] nostr_nip04_encrypt: Failed to allocate memory for ciphertext\n");
free(padded_data);
return NOSTR_ERROR_MEMORY_FAILED;
}
if (aes_cbc_encrypt(shared_secret, iv, padded_data, actual_padded_len, ciphertext) != 0) {
printf("[DEBUG] nostr_nip04_encrypt: AES-256-CBC encryption failed\n");
free(padded_data);
free(ciphertext);
return NOSTR_ERROR_CRYPTO_FAILED;
}
printf("[DEBUG] nostr_nip04_encrypt: AES-256-CBC encryption completed successfully\n");
// Step 5: Base64 encode ciphertext and IV
printf("[DEBUG] nostr_nip04_encrypt: Base64 encoding ciphertext and IV\n");
size_t ciphertext_b64_len = ((actual_padded_len + 2) / 3) * 4 + 1;
size_t iv_b64_len = ((16 + 2) / 3) * 4 + 1;
@@ -195,7 +172,6 @@ int nostr_nip04_encrypt(const unsigned char* sender_private_key,
char* iv_b64 = malloc(iv_b64_len);
if (!ciphertext_b64 || !iv_b64) {
printf("[DEBUG] nostr_nip04_encrypt: Failed to allocate memory for base64 buffers\n");
free(padded_data);
free(ciphertext);
free(ciphertext_b64);
@@ -207,11 +183,8 @@ int nostr_nip04_encrypt(const unsigned char* sender_private_key,
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);
printf("[DEBUG] nostr_nip04_encrypt: Base64 encoding results - ct_b64_len=%zu, iv_b64_len_actual=%zu\n", ct_b64_len, iv_b64_len_actual);
// FIX: Check if encoding succeeded
if (ct_b64_len == 0 || iv_b64_len_actual == 0) {
printf("[DEBUG] nostr_nip04_encrypt: Base64 encoding failed\n");
free(padded_data);
free(ciphertext);
free(ciphertext_b64);
@@ -219,16 +192,11 @@ int nostr_nip04_encrypt(const unsigned char* sender_private_key,
memory_clear(shared_secret, 32);
return NOSTR_ERROR_CRYPTO_FAILED;
}
printf("[DEBUG] nostr_nip04_encrypt: Base64 encoding completed successfully\n");
// Step 6: Format as "ciphertext?iv=iv_base64" - size check moved earlier, now guaranteed to fit
printf("[DEBUG] nostr_nip04_encrypt: Formatting final output string\n");
size_t result_len = ct_b64_len + 4 + iv_b64_len_actual + 1; // +4 for "?iv=", +1 for null
printf("[DEBUG] nostr_nip04_encrypt: Final size check - result_len=%zu, output_size=%zu\n", result_len, output_size);
if (result_len > output_size) {
printf("[DEBUG] nostr_nip04_encrypt: Final output buffer too small (%zu > %zu)\n", result_len, output_size);
free(padded_data);
free(ciphertext);
free(ciphertext_b64);
@@ -238,8 +206,6 @@ int nostr_nip04_encrypt(const unsigned char* sender_private_key,
}
snprintf(output, output_size, "%s?iv=%s", ciphertext_b64, iv_b64);
printf("[DEBUG] nostr_nip04_encrypt: Formatted output successfully\n");
printf("[DEBUG] nostr_nip04_encrypt: Encryption completed successfully - returning NOSTR_SUCCESS\n");
// Cleanup
memory_clear(shared_secret, 32);
+5 -6
View File
@@ -27,6 +27,7 @@ static void memory_clear(const void *p, size_t len) {
}
}
// =============================================================================
// NIP-44 UTILITY FUNCTIONS
// =============================================================================
@@ -85,13 +86,8 @@ static char* unpad_plaintext(const unsigned char* padded, size_t padded_len) {
size_t unpadded_len = (padded[0] << 8) | padded[1];
size_t expected_padded_len = calc_padded_len(unpadded_len);
printf("--- unpad_plaintext DEBUG ---\n");
printf("padded_len: %zu\n", padded_len);
printf("unpadded_len: %zu\n", unpadded_len);
printf("expected_padded_len: %zu\n", expected_padded_len);
printf("--- end unpad_plaintext DEBUG ---\n");
if (padded_len != expected_padded_len) {
if (padded_len != expected_padded_len + 2) {
return NULL;
}
@@ -130,6 +126,7 @@ int nostr_nip44_encrypt_with_nonce(const unsigned char* sender_private_key,
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) {
@@ -150,6 +147,7 @@ int nostr_nip44_encrypt_with_nonce(const unsigned char* sender_private_key,
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) {
@@ -163,6 +161,7 @@ int nostr_nip44_encrypt_with_nonce(const unsigned char* sender_private_key,
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);
+2 -23
View File
@@ -174,14 +174,12 @@ int ecdh_shared_secret(const unsigned char* private_key,
* nostr_cleanup();
*/
printf("[DEBUG] ecdh_shared_secret: Starting ECDH computation\n");
if (!private_key || !public_key_x || !shared_secret) {
printf("[DEBUG] ecdh_shared_secret: Invalid input - null pointer detected\n");
return -1;
}
printf("[DEBUG] ecdh_shared_secret: Input validation passed\n");
// NIP-04 ECDH: The key insight from the specification is that NOSTR requires
// "only the X coordinate of the shared point is used as the secret and it is NOT hashed"
@@ -196,21 +194,11 @@ int ecdh_shared_secret(const unsigned char* private_key,
compressed_pubkey[0] = 0x02;
memcpy(compressed_pubkey + 1, public_key_x, 32);
printf("[DEBUG] ecdh_shared_secret: Trying 0x02 prefix (even y)\n");
if (nostr_secp256k1_ec_pubkey_parse(&pubkey, compressed_pubkey, 33) == 1) {
printf("[DEBUG] ecdh_shared_secret: 0x02 prefix worked, public key parsed successfully\n");
// Perform ECDH with our custom hash function that copies the X coordinate
printf("[DEBUG] ecdh_shared_secret: Performing ECDH operation with 0x02 prefix\n");
if (nostr_secp256k1_ecdh(shared_secret, &pubkey, private_key, ecdh_hash_function_copy_x, NULL) == 1) {
printf("[DEBUG] ecdh_shared_secret: ECDH operation completed successfully\n");
return 0;
} else {
printf("[DEBUG] ecdh_shared_secret: ECDH operation failed with 0x02 prefix\n");
}
} else {
printf("[DEBUG] ecdh_shared_secret: 0x02 prefix failed, trying 0x03 prefix (odd y)\n");
}
// Try with 0x03 prefix (odd y-coordinate)
@@ -218,21 +206,12 @@ int ecdh_shared_secret(const unsigned char* private_key,
// public_key_x is already copied above
if (nostr_secp256k1_ec_pubkey_parse(&pubkey, compressed_pubkey, 33) == 1) {
printf("[DEBUG] ecdh_shared_secret: 0x03 prefix worked, public key parsed successfully\n");
// Perform ECDH with our custom hash function that copies the X coordinate
printf("[DEBUG] ecdh_shared_secret: Performing ECDH operation with 0x03 prefix\n");
if (nostr_secp256k1_ecdh(shared_secret, &pubkey, private_key, ecdh_hash_function_copy_x, NULL) == 1) {
printf("[DEBUG] ecdh_shared_secret: ECDH operation completed successfully\n");
return 0;
} else {
printf("[DEBUG] ecdh_shared_secret: ECDH operation failed with 0x03 prefix\n");
}
} else {
printf("[DEBUG] ecdh_shared_secret: Both 0x02 and 0x03 prefixes failed - invalid public key\n");
}
printf("[DEBUG] ecdh_shared_secret: All attempts failed\n");
return -1;
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+8 -6
View File
@@ -83,12 +83,6 @@ static int hex_to_bytes(const char* hex, unsigned char* bytes, size_t len) {
return 0;
}
static void bytes_to_hex(const unsigned char* bytes, size_t len, char* hex) {
for (size_t i = 0; i < len; i++) {
sprintf(hex + i * 2, "%02x", bytes[i]);
}
hex[len * 2] = '\0';
}
static int test_nip44_round_trip(const nip44_test_vector_t* tv) {
printf("Test: %s\n", tv->name);
@@ -364,31 +358,39 @@ int main() {
size_t num_vectors = sizeof(test_vectors) / sizeof(test_vectors[0]);
for (size_t i = 0; i < num_vectors; i++) {
total_tests++;
printf("Test #%d\n", total_tests);
if (test_nip44_round_trip(&test_vectors[i]) == 0) {
passed_tests++;
}
printf("\n");
}
// Test decryption vectors (cross-compatibility)
size_t num_decryption_vectors = sizeof(decryption_test_vectors) / sizeof(decryption_test_vectors[0]);
for (size_t i = 0; i < num_decryption_vectors; i++) {
total_tests++;
printf("Test #%d\n", total_tests);
if (test_nip44_decryption_vector(&decryption_test_vectors[i]) == 0) {
passed_tests++;
}
printf("\n");
}
// Test encryption variability (NIP-44 non-deterministic behavior)
total_tests++;
printf("Test #%d\n", total_tests);
if (test_nip44_encryption_variability() == 0) {
passed_tests++;
}
printf("\n");
// Test error conditions
total_tests++;
printf("Test #%d\n", total_tests);
if (test_nip44_error_conditions() == 0) {
passed_tests++;
}
printf("\n");
// Final results
printf("\nTest Results: %d/%d passed\n", passed_tests, total_tests);
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.