mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-28 08:22:24 +00:00
Add NIP-44 verification to DM decrypt worker
This commit is contained in:
1 parent
2b79021184
commit
4cc51d7a79
5 files changed
+76
-8
No files matched your search
+10
-5
@@ -268,6 +268,7 @@ function buildDecryptResult({
|
||||
envelope = null,
|
||||
errors = [],
|
||||
}) {
|
||||
const normalizedScheme = normalizeScheme(scheme || decryptor?.scheme || "");
|
||||
const timestampCandidates = [];
|
||||
if (message && Number.isFinite(message.created_at)) {
|
||||
timestampCandidates.push(message.created_at);
|
||||
@@ -295,11 +296,12 @@ function buildDecryptResult({
|
||||
actorPubkey: normalizeHex(actorPubkey),
|
||||
decryptor: decryptor
|
||||
? {
|
||||
scheme: normalizeScheme(decryptor.scheme) || scheme || "",
|
||||
scheme: normalizeScheme(decryptor.scheme) || normalizedScheme || "",
|
||||
source: decryptor.source || "",
|
||||
}
|
||||
: { scheme: scheme || "", source: "" },
|
||||
scheme: normalizeScheme(scheme || decryptor?.scheme || ""),
|
||||
: { scheme: normalizedScheme || "", source: "" },
|
||||
scheme: normalizedScheme,
|
||||
encryption_scheme: normalizedScheme,
|
||||
envelope,
|
||||
direction: deriveDirection(actorPubkey, senderPubkey, recipients),
|
||||
timestamp: Number.isFinite(timestamp) ? timestamp : Date.now() / 1000,
|
||||
@@ -472,6 +474,10 @@ async function decryptLegacyDm(event, decryptors, actorPubkey) {
|
||||
});
|
||||
|
||||
if (typeof plaintext === "string") {
|
||||
const resolvedScheme =
|
||||
normalizeScheme(hints.algorithms?.[0]) ||
|
||||
normalizeScheme(decryptor.scheme) ||
|
||||
"";
|
||||
return buildDecryptResult({
|
||||
ok: true,
|
||||
event,
|
||||
@@ -484,7 +490,7 @@ async function decryptLegacyDm(event, decryptors, actorPubkey) {
|
||||
senderPubkey,
|
||||
actorPubkey,
|
||||
decryptor,
|
||||
scheme: decryptor.scheme || hints.algorithms?.[0] || "",
|
||||
scheme: resolvedScheme,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -586,4 +592,3 @@ export const __testUtils = {
|
||||
collectRecipients,
|
||||
orderDecryptors,
|
||||
};
|
||||
|
||||
+4
-2
@@ -4066,12 +4066,13 @@ export class NostrClient {
|
||||
if (canUseWorker) {
|
||||
addCandidate(
|
||||
"nip44",
|
||||
(targetPubkey, ciphertext) =>
|
||||
(targetPubkey, ciphertext, options = {}) =>
|
||||
decryptDmInWorker({
|
||||
scheme: "nip44",
|
||||
privateKey: sessionPrivateKey,
|
||||
targetPubkey,
|
||||
ciphertext,
|
||||
event: options?.event,
|
||||
}),
|
||||
{
|
||||
priority: -6,
|
||||
@@ -4082,12 +4083,13 @@ export class NostrClient {
|
||||
|
||||
addCandidate(
|
||||
"nip04",
|
||||
(targetPubkey, ciphertext) =>
|
||||
(targetPubkey, ciphertext, options = {}) =>
|
||||
decryptDmInWorker({
|
||||
scheme: "nip04",
|
||||
privateKey: sessionPrivateKey,
|
||||
targetPubkey,
|
||||
ciphertext,
|
||||
event: options?.event,
|
||||
}),
|
||||
{
|
||||
priority: -4,
|
||||
|
||||
@@ -47,6 +47,8 @@ const normalizePayload = (payload) => {
|
||||
typeof payload.targetPubkey === "string" ? payload.targetPubkey.trim() : "";
|
||||
const ciphertext =
|
||||
typeof payload.ciphertext === "string" ? payload.ciphertext : "";
|
||||
const event =
|
||||
payload.event && typeof payload.event === "object" ? payload.event : null;
|
||||
|
||||
if (!id || !privateKey || !targetPubkey || !ciphertext) {
|
||||
return null;
|
||||
@@ -58,6 +60,33 @@ const normalizePayload = (payload) => {
|
||||
privateKey,
|
||||
targetPubkey,
|
||||
ciphertext,
|
||||
event,
|
||||
};
|
||||
};
|
||||
|
||||
const normalizeEventForVerification = (event) => {
|
||||
if (!event || typeof event !== "object") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pubkey = typeof event.pubkey === "string" ? event.pubkey.trim() : "";
|
||||
const sig = typeof event.sig === "string" ? event.sig.trim() : "";
|
||||
const id = typeof event.id === "string" ? event.id.trim() : "";
|
||||
|
||||
if (!pubkey || !sig || !id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
pubkey,
|
||||
sig,
|
||||
kind: Number.isFinite(event.kind) ? event.kind : 0,
|
||||
created_at: Number.isFinite(event.created_at)
|
||||
? event.created_at
|
||||
: Math.floor(Date.now() / 1000),
|
||||
tags: Array.isArray(event.tags) ? event.tags : [],
|
||||
content: typeof event.content === "string" ? event.content : "",
|
||||
};
|
||||
};
|
||||
|
||||
@@ -137,6 +166,31 @@ const ensureNip44 = async () => {
|
||||
};
|
||||
};
|
||||
|
||||
const verifyEventSignature = async (event, tools = null) => {
|
||||
const candidate = normalizeEventForVerification(event);
|
||||
if (!candidate) {
|
||||
throw new Error("missing-event-signature");
|
||||
}
|
||||
|
||||
const resolvedTools = tools || (await ensureNostrTools()) || getCachedNostrTools();
|
||||
if (!resolvedTools || typeof resolvedTools !== "object") {
|
||||
throw new Error("signature-verification-unavailable");
|
||||
}
|
||||
|
||||
if (
|
||||
typeof resolvedTools.validateEvent !== "function" ||
|
||||
typeof resolvedTools.verifyEvent !== "function"
|
||||
) {
|
||||
throw new Error("signature-verification-unavailable");
|
||||
}
|
||||
|
||||
if (!resolvedTools.validateEvent(candidate) || !resolvedTools.verifyEvent(candidate)) {
|
||||
throw new Error("invalid-event-signature");
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleMessage = async (event) => {
|
||||
const payload = normalizePayload(event?.data);
|
||||
if (!payload) {
|
||||
@@ -159,6 +213,7 @@ const handleMessage = async (event) => {
|
||||
|
||||
try {
|
||||
if (payload.scheme === "nip44" || payload.scheme === "nip44_v2") {
|
||||
await verifyEventSignature(payload.event);
|
||||
const nip44 = await ensureNip44();
|
||||
const privateKeyBytes = nip44.hexToBytes(payload.privateKey);
|
||||
const conversationKey = nip44.getConversationKey(
|
||||
|
||||
@@ -82,6 +82,7 @@ export function decryptDmInWorker({
|
||||
privateKey,
|
||||
targetPubkey,
|
||||
ciphertext,
|
||||
event,
|
||||
timeoutMs = DEFAULT_TIMEOUT_MS,
|
||||
} = {}) {
|
||||
const worker = ensureWorker();
|
||||
@@ -95,6 +96,7 @@ export function decryptDmInWorker({
|
||||
typeof targetPubkey === "string" ? targetPubkey.trim() : "";
|
||||
const payloadCiphertext = typeof ciphertext === "string" ? ciphertext : "";
|
||||
const payloadScheme = typeof scheme === "string" ? scheme : "";
|
||||
const payloadEvent = event && typeof event === "object" ? event : null;
|
||||
|
||||
if (!trimmedPrivateKey || !trimmedTarget || !payloadCiphertext) {
|
||||
return Promise.reject(new Error("dm-worker-invalid-input"));
|
||||
@@ -115,6 +117,7 @@ export function decryptDmInWorker({
|
||||
privateKey: trimmedPrivateKey,
|
||||
targetPubkey: trimmedTarget,
|
||||
ciphertext: payloadCiphertext,
|
||||
event: payloadEvent,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -353,7 +353,9 @@ function normalizeDirectMessageRecord(message, actorPubkey = "") {
|
||||
: [];
|
||||
|
||||
const encryptionScheme =
|
||||
typeof message?.scheme === "string"
|
||||
typeof message?.encryption_scheme === "string"
|
||||
? message.encryption_scheme.trim()
|
||||
: typeof message?.scheme === "string"
|
||||
? message.scheme.trim()
|
||||
: typeof message?.decryptor?.scheme === "string"
|
||||
? message.decryptor.scheme.trim()
|
||||
@@ -442,6 +444,7 @@ function hydrateMessagesFromSnapshot(snapshot, actorPubkey = "") {
|
||||
recipients: [],
|
||||
direction: "unknown",
|
||||
scheme: "",
|
||||
encryption_scheme: "",
|
||||
decryptor: { scheme: "", source: "snapshot" },
|
||||
event: {
|
||||
id: `dm-snapshot:${normalizedActor || "actor"}:${remote}:${timestamp}:${index}`,
|
||||
|
||||
Reference in new issue
Block a user