// Debug script to extract all intermediate values from nostr-tools NIP-44 const { v2 } = require('./nostr-tools/nip44.js'); const { bytesToHex, hexToBytes } = require('@noble/hashes/utils'); // Test vector 1: single char 'a' console.log('=== NOSTR-TOOLS DEBUG: Single char "a" ==='); const sec1 = hexToBytes('0000000000000000000000000000000000000000000000000000000000000001'); const sec2 = hexToBytes('0000000000000000000000000000000000000000000000000000000000000002'); const nonce = hexToBytes('0000000000000000000000000000000000000000000000000000000000000001'); const plaintext = 'a'; // Step 1: Get public keys const { schnorr } = require('@noble/curves/secp256k1'); const pub1 = bytesToHex(schnorr.getPublicKey(sec1)); const pub2 = bytesToHex(schnorr.getPublicKey(sec2)); console.log('pub1:', pub1); console.log('pub2:', pub2); // Step 2: Get conversation key const conversationKey = v2.utils.getConversationKey(sec1, pub2); console.log('conversation_key:', bytesToHex(conversationKey)); // Step 3: Get shared secret (raw ECDH) const { secp256k1 } = require('@noble/curves/secp256k1'); const sharedPoint = secp256k1.getSharedSecret(sec1, '02' + pub2); const sharedSecret = sharedPoint.subarray(1, 33); // X coordinate only console.log('ecdh_shared_secret:', bytesToHex(sharedSecret)); // Step 4: Get message keys using internal function const hkdf = require('@noble/hashes/hkdf'); const { sha256 } = require('@noble/hashes/sha256'); // HKDF Extract step const salt = new TextEncoder().encode('nip44-v2'); const prk = hkdf.extract(sha256, sharedSecret, salt); console.log('hkdf_extract_result:', bytesToHex(prk)); // HKDF Expand step const messageKeys = hkdf.expand(sha256, prk, nonce, 76); const chachaKey = messageKeys.subarray(0, 32); const chachaNonce = messageKeys.subarray(32, 44); const hmacKey = messageKeys.subarray(44, 76); console.log('chacha_key:', bytesToHex(chachaKey)); console.log('chacha_nonce:', bytesToHex(chachaNonce)); console.log('hmac_key:', bytesToHex(hmacKey)); // Step 5: Pad the plaintext function pad(plaintext) { const utf8Encoder = new TextEncoder(); const unpadded = utf8Encoder.encode(plaintext); const unpaddedLen = unpadded.length; // Length prefix (big-endian u16) const prefix = new Uint8Array(2); new DataView(prefix.buffer).setUint16(0, unpaddedLen, false); // Calculate padded length function calcPaddedLen(len) { if (len <= 32) return 32; const nextPower = 1 << (Math.floor(Math.log2(len - 1)) + 1); const chunk = nextPower <= 256 ? 32 : nextPower / 8; return chunk * (Math.floor((len - 1) / chunk) + 1); } const paddedLen = calcPaddedLen(unpaddedLen + 2); const suffix = new Uint8Array(paddedLen - 2 - unpaddedLen); // Combine: prefix + plaintext + padding const result = new Uint8Array(paddedLen); result.set(prefix); result.set(unpadded, 2); result.set(suffix, 2 + unpaddedLen); return result; } const paddedPlaintext = pad(plaintext); console.log('padded_plaintext:', bytesToHex(paddedPlaintext)); console.log('padded_length:', paddedPlaintext.length); // Step 6: ChaCha20 encrypt const { chacha20 } = require('@noble/ciphers/chacha'); const ciphertext = chacha20(chachaKey, chachaNonce, paddedPlaintext); console.log('ciphertext:', bytesToHex(ciphertext)); // Step 7: HMAC with AAD const { hmac } = require('@noble/hashes/hmac'); const { concatBytes } = require('@noble/hashes/utils'); const aad = concatBytes(nonce, ciphertext); console.log('aad_data:', bytesToHex(aad)); const mac = hmac(sha256, hmacKey, aad); console.log('mac:', bytesToHex(mac)); // Step 8: Final payload const { base64 } = require('@scure/base'); const payload = concatBytes(new Uint8Array([2]), nonce, ciphertext, mac); console.log('raw_payload:', bytesToHex(payload)); const base64Payload = base64.encode(payload); console.log('final_payload:', base64Payload); // Expected from test vectors console.log('expected_payload:', 'AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb'); // Now let's also test the full encrypt function const fullEncrypt = v2.encrypt(plaintext, conversationKey, nonce); console.log('v2.encrypt_result:', fullEncrypt);