|
|
|
|
@@ -75,54 +75,55 @@ static size_t calc_padded_len(size_t unpadded_len) {
|
|
|
|
|
return chunk * ((unpadded_len - 1) / chunk + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NIP-44 padding (per spec)
|
|
|
|
|
// NIP-44 padding (modified to support 1MB payloads with 32-bit length field)
|
|
|
|
|
static unsigned char* pad_plaintext(const char* plaintext, size_t* padded_len) {
|
|
|
|
|
size_t unpadded_len = strlen(plaintext);
|
|
|
|
|
if (unpadded_len > 65535) {
|
|
|
|
|
if (unpadded_len > 1048576) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t padded_content_len = calc_padded_len(unpadded_len);
|
|
|
|
|
*padded_len = padded_content_len + 2; // Add 2 bytes for the length prefix
|
|
|
|
|
*padded_len = padded_content_len + 4; // Add 4 bytes for the length prefix (32-bit)
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Write length prefix (big-endian u32)
|
|
|
|
|
padded[0] = (unpadded_len >> 24) & 0xFF;
|
|
|
|
|
padded[1] = (unpadded_len >> 16) & 0xFF;
|
|
|
|
|
padded[2] = (unpadded_len >> 8) & 0xFF;
|
|
|
|
|
padded[3] = unpadded_len & 0xFF;
|
|
|
|
|
|
|
|
|
|
// Copy plaintext and add zero-padding
|
|
|
|
|
memcpy(padded + 2, plaintext, unpadded_len);
|
|
|
|
|
memset(padded + 2 + unpadded_len, 0, padded_content_len - unpadded_len);
|
|
|
|
|
|
|
|
|
|
memcpy(padded + 4, plaintext, unpadded_len);
|
|
|
|
|
memset(padded + 4 + unpadded_len, 0, padded_content_len - unpadded_len);
|
|
|
|
|
|
|
|
|
|
return padded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NIP-44 unpadding (per spec)
|
|
|
|
|
// NIP-44 unpadding (modified to support 32-bit length field)
|
|
|
|
|
static char* unpad_plaintext(const unsigned char* padded, size_t padded_len) {
|
|
|
|
|
if (padded_len < 4) return NULL;
|
|
|
|
|
if (padded_len < 6) return NULL; // Minimum: 4 bytes length + 2 bytes content
|
|
|
|
|
|
|
|
|
|
size_t unpadded_len = (padded[0] << 8) | padded[1];
|
|
|
|
|
size_t unpadded_len = (padded[0] << 24) | (padded[1] << 16) | (padded[2] << 8) | padded[3];
|
|
|
|
|
size_t expected_padded_len = calc_padded_len(unpadded_len);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (padded_len != expected_padded_len + 2) {
|
|
|
|
|
if (padded_len != expected_padded_len + 4) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (unpadded_len > padded_len - 2) {
|
|
|
|
|
if (unpadded_len > padded_len - 4) {
|
|
|
|
|
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);
|
|
|
|
|
memcpy(plaintext, padded + 4, unpadded_len);
|
|
|
|
|
}
|
|
|
|
|
plaintext[unpadded_len] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return plaintext;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -131,15 +132,15 @@ static char* unpad_plaintext(const unsigned char* padded, size_t padded_len) {
|
|
|
|
|
// =============================================================================
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
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;
|
|
|
|
|
@@ -260,12 +261,12 @@ int nostr_nip44_encrypt_with_nonce(const unsigned char* sender_private_key,
|
|
|
|
|
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) {
|
|
|
|
|
@@ -315,18 +316,18 @@ int nostr_nip44_encrypt_with_nonce(const unsigned char* sender_private_key,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
@@ -421,7 +422,6 @@ int nostr_nip44_decrypt(const unsigned char* recipient_private_key,
|
|
|
|
|
return NOSTR_ERROR_CRYPTO_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Constant-time MAC verification
|
|
|
|
|
// Constant-time MAC verification
|
|
|
|
|
if (!constant_time_compare(received_mac, computed_mac, 32)) {
|
|
|
|
|
memory_clear(shared_secret, 32);
|
|
|
|
|
@@ -446,7 +446,7 @@ int nostr_nip44_decrypt(const unsigned char* recipient_private_key,
|
|
|
|
|
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);
|
|
|
|
|
@@ -459,7 +459,6 @@ int nostr_nip44_decrypt(const unsigned char* recipient_private_key,
|
|
|
|
|
return NOSTR_ERROR_CRYPTO_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Step 8: Remove padding according to NIP-44 spec
|
|
|
|
|
// Step 8: Remove padding according to NIP-44 spec
|
|
|
|
|
char* plaintext = unpad_plaintext(padded_plaintext, ciphertext_len);
|
|
|
|
|
if (!plaintext) {
|
|
|
|
|
|