Fix NWC encryption fallback handling

This commit is contained in:
thePR0M3TH3AN committed 2025-10-05 20:49:56 -04:00
1 parent 9d57a43c98
commit aa4e86758a
2 files changed
+99 -19

No files matched your search

+29 -19
View File
@@ -341,16 +341,21 @@ function createNip44Encryption(context) {
function getWalletSupportedEncryption(infoEvent) {
if (!infoEvent || !Array.isArray(infoEvent.tags)) {
return ["nip04"];
return { schemes: [], explicit: false };
}
const tag = infoEvent.tags.find((entry) => Array.isArray(entry) && entry[0] === "encryption");
if (!tag || typeof tag[1] !== "string") {
return ["nip04"];
return { schemes: [], explicit: false };
}
const values = normalizeSpaceSeparatedValues(tag[1]);
return values.length ? values : ["nip04"];
if (!values.length) {
return { schemes: [], explicit: false };
}
const schemes = Array.from(new Set(values));
return { schemes, explicit: true };
}
function getEncryptionCandidates(context) {
@@ -487,31 +492,35 @@ async function ensureEncryption(context) {
}
}
const walletSchemes = getWalletSupportedEncryption(infoEvent);
const { schemes: walletSchemes } = getWalletSupportedEncryption(infoEvent);
const candidates = getEncryptionCandidates(context).filter((candidate) => {
return !state.unsupported.has(candidate.scheme);
});
for (const candidate of candidates) {
if (walletSchemes.includes(candidate.scheme)) {
context.encryption = candidate;
return candidate;
}
const compatibleCandidates = walletSchemes.length
? candidates.filter((candidate) => walletSchemes.includes(candidate.scheme))
: candidates;
if (compatibleCandidates.length) {
const selected = compatibleCandidates[0];
context.encryption = selected;
return selected;
}
const fallback = candidates.find((candidate) => candidate.scheme === "nip04");
if (fallback) {
console.warn(
"[nwcClient] Falling back to nip04 encryption despite wallet not advertising support."
);
context.encryption = fallback;
return fallback;
}
if (walletSchemes.includes("nip04")) {
if (
walletSchemes.includes("nip04") &&
!candidates.some((candidate) => candidate.scheme === "nip04")
) {
throw new Error("NostrTools.nip04.encrypt is not available.");
}
if (walletSchemes.length) {
const message = `Wallet advertises unsupported encryption schemes: ${walletSchemes.join(", ")}.`;
const error = new Error(message);
error.code = "UNSUPPORTED_ENCRYPTION";
throw error;
}
throw new Error("No compatible wallet encryption scheme available.");
}
@@ -983,4 +992,5 @@ export const __TESTING__ = Object.freeze({
ensureActiveState,
buildPayInvoiceParams,
getActiveState: () => activeState,
ensureEncryptionForContext: ensureEncryption,
});
+70
View File
@@ -48,6 +48,21 @@ await (async () => {
getPublicKey() {
return "c".repeat(64);
},
nip44: {
v2: {
async encrypt(plaintext, key) {
return `nip44:${key}:${plaintext}`;
},
async decrypt(ciphertext, key) {
return `nip44:${key}:${ciphertext}`;
},
utils: {
getConversationKey(secretBytes, pubkey) {
return `key-${pubkey}-${secretBytes.length}`;
},
},
},
},
};
const walletPubkey = "b".repeat(64);
@@ -75,6 +90,61 @@ await (async () => {
assert.equal(reparsed.secretKey, secretKey);
assert.equal(reparsed.queryParams.lud16, lud16);
context.infoEvent = {
kind: 13194,
tags: [["encryption", "nip44_v2"]],
};
const encryption = await __TESTING__.ensureEncryptionForContext(context);
assert.equal(encryption.scheme, "nip44_v2");
const encrypted = await encryption.encrypt("payload");
assert.match(encrypted, /^nip44:key-b{64}-/);
resetWalletClient();
})();
await (async () => {
resetWalletClient();
window.NostrTools = {
getPublicKey() {
return "c".repeat(64);
},
nip04: {
async encrypt() {
return "nip04";
},
async decrypt() {
return "nip04";
},
},
};
const walletPubkey = "b".repeat(64);
const secretKey = "a".repeat(64);
const relays = ["wss://relay.one.example"];
const uri =
`nostr+walletconnect://${walletPubkey}` +
`?relay=${encodeURIComponent(relays[0])}` +
`&secret=${secretKey}`;
const context = ensureActiveState({ nwcUri: uri });
context.infoEvent = {
kind: 13194,
tags: [["encryption", "nip44_v2"]],
};
await assert.rejects(
() => __TESTING__.ensureEncryptionForContext(context),
(error) => {
assert.match(
error?.message || "",
/Wallet advertises unsupported encryption schemes: nip44_v2\./
);
return true;
}
);
resetWalletClient();
})();