mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-02 02:42:28 +00:00
Handle buffer-encoded NIP-46 handshake payloads
This commit is contained in:
1 parent
18d87de7f2
commit
e7497a559c
2 files changed
+158
No files matched your search
+94
@@ -3636,6 +3636,83 @@ function resolveLegacyNip44ConversationKeyGetter(tools) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let sharedTextDecoder = null;
|
||||
|
||||
function decodeBytesToUtf8(value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (typeof Buffer !== "undefined" && typeof Buffer.isBuffer === "function" && Buffer.isBuffer(value)) {
|
||||
try {
|
||||
return value.toString("utf8");
|
||||
} catch (error) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
let view = null;
|
||||
|
||||
if (value instanceof Uint8Array) {
|
||||
view = value;
|
||||
} else if (typeof ArrayBuffer !== "undefined") {
|
||||
if (value instanceof ArrayBuffer) {
|
||||
view = new Uint8Array(value);
|
||||
} else if (typeof ArrayBuffer.isView === "function" && ArrayBuffer.isView(value)) {
|
||||
try {
|
||||
view = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
||||
} catch (error) {
|
||||
view = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!view && Array.isArray(value)) {
|
||||
const bytes = [];
|
||||
for (const entry of value) {
|
||||
if (typeof entry === "number" && Number.isFinite(entry)) {
|
||||
const normalized = entry & 0xff;
|
||||
if (normalized < 0 || normalized > 255) {
|
||||
return "";
|
||||
}
|
||||
bytes.push(normalized);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
if (!bytes.length) {
|
||||
return "";
|
||||
}
|
||||
view = Uint8Array.from(bytes);
|
||||
}
|
||||
|
||||
if (!view || !view.length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!sharedTextDecoder && typeof TextDecoder === "function") {
|
||||
try {
|
||||
sharedTextDecoder = new TextDecoder();
|
||||
} catch (error) {
|
||||
sharedTextDecoder = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (sharedTextDecoder) {
|
||||
try {
|
||||
return sharedTextDecoder.decode(view);
|
||||
} catch (error) {
|
||||
// fall through to manual decoding
|
||||
}
|
||||
}
|
||||
|
||||
let result = "";
|
||||
for (let index = 0; index < view.length; index += 1) {
|
||||
result += String.fromCharCode(view[index]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function normalizeNip46CiphertextPayload(payload) {
|
||||
const visited = new Set();
|
||||
|
||||
@@ -3667,6 +3744,11 @@ function normalizeNip46CiphertextPayload(payload) {
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const decoded = decodeBytesToUtf8(value);
|
||||
if (decoded) {
|
||||
return decoded.trim();
|
||||
}
|
||||
|
||||
const parts = [];
|
||||
for (const entry of value) {
|
||||
const normalized = coerce(entry);
|
||||
@@ -3684,6 +3766,18 @@ function normalizeNip46CiphertextPayload(payload) {
|
||||
}
|
||||
|
||||
if (value && typeof value === "object") {
|
||||
const decoded = decodeBytesToUtf8(value);
|
||||
if (decoded) {
|
||||
return decoded.trim();
|
||||
}
|
||||
|
||||
if (typeof value.type === "string" && value.type.toLowerCase() === "buffer" && Array.isArray(value.data)) {
|
||||
const bufferDecoded = decodeBytesToUtf8(value.data);
|
||||
if (bufferDecoded) {
|
||||
return bufferDecoded.trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (visited.has(value)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { Buffer } from "node:buffer";
|
||||
import test from "node:test";
|
||||
|
||||
const PAYLOAD = { message: "hello", count: 42 };
|
||||
@@ -131,3 +132,66 @@ test("decryptNip46PayloadWithKeys coerces structured handshake payloads", async
|
||||
"helper should also decrypt JSON-serialized handshake payloads",
|
||||
);
|
||||
});
|
||||
|
||||
test("decryptNip46PayloadWithKeys decodes buffer-based handshake payloads", async () => {
|
||||
const nostrTools = await loadNostrTools();
|
||||
const { generateSecretKey, getPublicKey, nip44, utils } = nostrTools;
|
||||
|
||||
const clientSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remoteSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remotePubkey = getPublicKey(remoteSecret);
|
||||
|
||||
const serialized = JSON.stringify(PAYLOAD);
|
||||
const conversationKey =
|
||||
typeof nip44.v2.getConversationKey === "function"
|
||||
? nip44.v2.getConversationKey(clientSecret, remotePubkey)
|
||||
: nip44.v2.utils.getConversationKey(clientSecret, remotePubkey);
|
||||
const ciphertext = nip44.v2.encrypt(serialized, conversationKey);
|
||||
const ciphertextBuffer = Buffer.from(ciphertext, "utf8");
|
||||
|
||||
const bufferPayload = ciphertextBuffer;
|
||||
|
||||
const { __testExports } = await import("../js/nostr.js");
|
||||
const { decryptNip46PayloadWithKeys } = __testExports;
|
||||
|
||||
const bufferRoundTrip = await decryptNip46PayloadWithKeys(
|
||||
clientSecret,
|
||||
remotePubkey,
|
||||
bufferPayload,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
bufferRoundTrip,
|
||||
serialized,
|
||||
"helper should decrypt handshake payloads that serialize buffers",
|
||||
);
|
||||
|
||||
const jsonBufferPayload = JSON.stringify(bufferPayload);
|
||||
const jsonBufferRoundTrip = await decryptNip46PayloadWithKeys(
|
||||
clientSecret,
|
||||
remotePubkey,
|
||||
jsonBufferPayload,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
jsonBufferRoundTrip,
|
||||
serialized,
|
||||
"helper should decrypt JSON-encoded buffer handshake payloads",
|
||||
);
|
||||
|
||||
const objectBufferPayload = {
|
||||
ciphertext: ciphertextBuffer,
|
||||
};
|
||||
|
||||
const objectBufferRoundTrip = await decryptNip46PayloadWithKeys(
|
||||
clientSecret,
|
||||
remotePubkey,
|
||||
objectBufferPayload,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
objectBufferRoundTrip,
|
||||
serialized,
|
||||
"helper should decrypt objects containing buffer encoded ciphertext",
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user