diff --git a/src/app/router/Prompt/Prompt.tsx b/src/app/router/Prompt/Prompt.tsx index 090d637b58..da9a42c5fe 100644 --- a/src/app/router/Prompt/Prompt.tsx +++ b/src/app/router/Prompt/Prompt.tsx @@ -30,6 +30,8 @@ import WebbtcEnable from "~/app/screens/Enable/WebbtcEnable"; import WeblnEnable from "~/app/screens/Enable/WeblnEnable"; import NostrConfirmDecrypt from "~/app/screens/Nostr/ConfirmDecrypt"; import NostrConfirmEncrypt from "~/app/screens/Nostr/ConfirmEncrypt"; +import NostrConfirmNip44Decrypt from "~/app/screens/Nostr/ConfirmNip44Decrypt"; +import NostrConfirmNip44Encrypt from "~/app/screens/Nostr/ConfirmNip44Encrypt"; import type { NavigationState, OriginData } from "~/types"; // Parse out the parameters from the querystring. @@ -132,6 +134,14 @@ function Prompt() { path="public/nostr/confirmDecrypt" element={} /> + } + /> + } + /> } diff --git a/src/app/screens/Nostr/ConfirmNip44Decrypt.tsx b/src/app/screens/Nostr/ConfirmNip44Decrypt.tsx new file mode 100644 index 0000000000..a2e3033572 --- /dev/null +++ b/src/app/screens/Nostr/ConfirmNip44Decrypt.tsx @@ -0,0 +1,113 @@ + +import { CheckIcon } from "@bitcoin-design/bitcoin-icons-react/filled"; +import ConfirmOrCancel from "@components/ConfirmOrCancel"; +import Container from "@components/Container"; +import PublisherCard from "@components/PublisherCard"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import ScreenHeader from "~/app/components/ScreenHeader"; +import Checkbox from "~/app/components/form/Checkbox"; +import { useNavigationState } from "~/app/hooks/useNavigationState"; +import { USER_REJECTED_ERROR } from "~/common/constants"; +import msg from "~/common/lib/msg"; +import { OriginData } from "~/types"; + +function NostrConfirmNip44Decrypt() { + const { t } = useTranslation("translation", { + keyPrefix: "nostr", + }); + const { t: tPermissions } = useTranslation("permissions"); + const { t: tCommon } = useTranslation("common"); + const navState = useNavigationState(); + const origin = navState.origin as OriginData; + + const [loading, setLoading] = useState(false); + + const [rememberPermission, setRememberPermission] = useState(true); + + function confirm() { + setLoading(true); + msg.reply({ + confirm: true, + rememberPermission, + }); + setLoading(false); + } + + function reject(event: React.MouseEvent) { + event.preventDefault(); + msg.error(USER_REJECTED_ERROR); + } + + async function block(event: React.MouseEvent) { + event.preventDefault(); + await msg.request("addBlocklist", { + domain: origin.domain, + host: origin.host, + }); + alert(`Added ${origin.host} to the blocklist, please reload the website`); + msg.error(USER_REJECTED_ERROR); + } + + function handleSubmit(event: React.FormEvent) { + event.preventDefault(); + confirm(); + } + + return ( +
+ +
+ + +
+

{t("allow", { host: origin.host })}

+

+ + {tPermissions("nostr.nip44decrypt")} +

+
+ +
+
+ { + setRememberPermission(event.target.checked); + }} + /> + +
+ + + {t("block_and_ignore", { host: origin.host })} + +
+
+
+
+ ); +} + +export default NostrConfirmNip44Decrypt; diff --git a/src/app/screens/Nostr/ConfirmNip44Encrypt.tsx b/src/app/screens/Nostr/ConfirmNip44Encrypt.tsx new file mode 100644 index 0000000000..09c4b4ede9 --- /dev/null +++ b/src/app/screens/Nostr/ConfirmNip44Encrypt.tsx @@ -0,0 +1,121 @@ +import ConfirmOrCancel from "@components/ConfirmOrCancel"; +import Container from "@components/Container"; +import PublisherCard from "@components/PublisherCard"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import ContentMessage from "~/app/components/ContentMessage"; +import ScreenHeader from "~/app/components/ScreenHeader"; +import Checkbox from "~/app/components/form/Checkbox"; +import { useNavigationState } from "~/app/hooks/useNavigationState"; +import { USER_REJECTED_ERROR } from "~/common/constants"; +import nostr from "~/common/lib/nostr"; +import msg from "~/common/lib/msg"; +import { OriginData } from "~/types"; + +function NostrConfirmNip44Encrypt() { + const { t } = useTranslation("translation", { + keyPrefix: "nostr", + }); + const { t: tCommon } = useTranslation("common"); + const navState = useNavigationState(); + const origin = navState.origin as OriginData; + + const pairs = navState.args?.nip44Encrypt.pairs; + + const [loading, setLoading] = useState(false); + + const [rememberPermission, setRememberPermission] = useState(true); + + function confirm() { + setLoading(true); + msg.reply({ + confirm: true, + rememberPermission, + }); + setLoading(false); + } + + function reject(event: React.MouseEvent) { + event.preventDefault(); + msg.error(USER_REJECTED_ERROR); + } + + async function block(event: React.MouseEvent) { + event.preventDefault(); + await msg.request("addBlocklist", { + domain: origin.domain, + host: origin.host, + }); + alert(`Added ${origin.host} to the blocklist, please reload the website`); + msg.error(USER_REJECTED_ERROR); + } + + function handleSubmit(event: React.FormEvent) { + event.preventDefault(); + confirm(); + } + + return ( +
+ +
+ +
+ + {pairs?.map(([pubkey, text]) => ( + <> + +
+ {t("recipient")}: {nostr.hexToNip19(pubkey, "npub")} +
+ + ))} +
+
+
+ { + setRememberPermission(event.target.checked); + }} + /> + +
+ + + {t("block_and_ignore", { host: origin.host })} + +
+
+
+
+ ); +} + +export default NostrConfirmNip44Encrypt; diff --git a/src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts b/src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts index 186ffc37c6..a7d3d78bb8 100644 --- a/src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts @@ -8,10 +8,7 @@ import { import state from "~/extension/background-script/state"; import { MessageNip44DecryptGet, PermissionMethodNostr, Sender } from "~/types"; -const nip44DecryptOrPrompt = async ( - message: MessageNip44DecryptGet, - sender: Sender -) => { +const nip44DecryptOrPrompt = async (message: MessageNip44DecryptGet, sender: Sender) => { const host = getHostFromSender(sender); if (!host) return; @@ -23,10 +20,7 @@ const nip44DecryptOrPrompt = async ( if (hasPermission) { const nostr = await state.getState().getNostr(); - const response = await nostr.nip44Decrypt( - message.args.peer, - message.args.payload - ); + const response = await nostr.nip44Decrypt(message.args.pairs); return { data: response }; } else { @@ -35,14 +29,7 @@ const nip44DecryptOrPrompt = async ( rememberPermission: boolean; }>({ ...message, - action: "public/nostr/confirmEncryptOrDecrypt", - args: { - encryptOrDecrypt: { - action: "decrypt", - peer: message.args.peer, - payload: message.args.payload, - }, - }, + action: "public/nostr/confirmNip44Decrypt", }); // add permission to db only if user decided to always allow this request @@ -54,10 +41,7 @@ const nip44DecryptOrPrompt = async ( } if (promptResponse.data.confirm) { const nostr = await state.getState().getNostr(); - const response = await nostr.nip44Decrypt( - message.args.peer, - message.args.payload - ); + const response = await nostr.nip44Decrypt(message.args.pairs); return { data: response }; } else { diff --git a/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts b/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts index 2a0804aebd..a83b0a91a3 100644 --- a/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts @@ -8,10 +8,7 @@ import { import state from "~/extension/background-script/state"; import { MessageNip44EncryptGet, PermissionMethodNostr, Sender } from "~/types"; -const nip44EncryptOrPrompt = async ( - message: MessageNip44EncryptGet, - sender: Sender -) => { +const nip44EncryptOrPrompt = async (message: MessageNip44EncryptGet, sender: Sender) => { const host = getHostFromSender(sender); if (!host) return; @@ -22,11 +19,7 @@ const nip44EncryptOrPrompt = async ( ); if (hasPermission) { - const response = (await state.getState().getNostr()).nip44Encrypt( - message.args.peer, - message.args.plaintext, - message.args.v - ); + const response = (await state.getState().getNostr()).nip44Encrypt(message.args.pairs); return { data: response }; } else { const promptResponse = await utils.openPrompt<{ @@ -34,12 +27,10 @@ const nip44EncryptOrPrompt = async ( rememberPermission: boolean; }>({ ...message, - action: "public/nostr/confirmEncryptOrDecrypt", + action: "public/nostr/confirmNip44Encrypt", args: { - encryptOrDecrypt: { - action: "encrypt", - peer: message.args.peer, - message: message.args.plaintext, + nip44Encrypt: { + pairs: message.args.pairs, }, }, }); @@ -52,11 +43,7 @@ const nip44EncryptOrPrompt = async ( ); } if (promptResponse.data.confirm) { - const response = (await state.getState().getNostr()).nip44Encrypt( - message.args.peer, - message.args.plaintext, - message.args.v - ); + const response = (await state.getState().getNostr()).nip44Encrypt(message.args.pairs); return { data: response }; } else { diff --git a/src/extension/background-script/nostr/__test__/nostr.test.ts b/src/extension/background-script/nostr/__test__/nostr.test.ts index 3db5956d9a..8ebc3d8e9c 100644 --- a/src/extension/background-script/nostr/__test__/nostr.test.ts +++ b/src/extension/background-script/nostr/__test__/nostr.test.ts @@ -56,11 +56,11 @@ describe("nostr.nip44", () => { const aliceNostr = new Nostr(alice.privateKey); const message = "Secret message that is sent from Alice to Bob"; - const encrypted = aliceNostr.nip44Encrypt(bob.publicKey, message); + const [encrypted] = aliceNostr.nip44Encrypt([[bob.publicKey, message]]); const bobNostr = new Nostr(bob.privateKey); - const decrypted = await bobNostr.nip44Decrypt(alice.publicKey, encrypted); + const [decrypted] = await bobNostr.nip44Decrypt([[alice.publicKey, encrypted]]); expect(decrypted).toMatch(message); }); @@ -69,13 +69,13 @@ describe("nostr.nip44", () => { const aliceNostr = new Nostr(alice.privateKey); const message = "Secret message that is sent from Alice to Bob"; - const encrypted = aliceNostr.nip44Encrypt(bob.publicKey, message); + const [encrypted] = aliceNostr.nip44Encrypt([[bob.publicKey, message]]); const carolNostr = new Nostr(carol.privateKey); let decrypted; try { - decrypted = await carolNostr.nip44Decrypt(alice.publicKey, encrypted); + [decrypted] = await carolNostr.nip44Decrypt([[alice.publicKey, encrypted]]); } catch (e) { decrypted = "error decrypting message"; } diff --git a/src/extension/background-script/nostr/index.ts b/src/extension/background-script/nostr/index.ts index 4e6c9d8082..0ded5d39d6 100644 --- a/src/extension/background-script/nostr/index.ts +++ b/src/extension/background-script/nostr/index.ts @@ -82,12 +82,18 @@ class Nostr { return Utf8.stringify(decrypted); } - nip44Encrypt(pubkey: string, text: string) { - return nip44.v2.encrypt(text, this.getNip44SharedSecret(pubkey)); + nip44Encrypt(pairs: [string, string][]) { + return pairs.map( + ([pubkey, text]: [string, string]) => + nip44.v2.encrypt(text, this.getNip44SharedSecret(pubkey)) + ); } - nip44Decrypt(pubkey: string, payload: string) { - return nip44.v2.decrypt(payload, this.getNip44SharedSecret(pubkey)); + nip44Decrypt(pairs: [string, string][]) { + return pairs.map( + ([pubkey, payload]) => + nip44.v2.decrypt(payload, this.getNip44SharedSecret(pubkey)) + ); } getEventHash(event: Event) { diff --git a/src/extension/content-script/nostr.js b/src/extension/content-script/nostr.js index e3d8a287d2..3b67440a03 100644 --- a/src/extension/content-script/nostr.js +++ b/src/extension/content-script/nostr.js @@ -13,6 +13,8 @@ const nostrCalls = [ "nostr/enable", "nostr/encryptOrPrompt", "nostr/decryptOrPrompt", + "nostr/nip44EncryptOrPrompt", + "nostr/nip44DecryptOrPrompt", "nostr/on", "nostr/off", "nostr/emit", diff --git a/src/extension/providers/nostr/index.ts b/src/extension/providers/nostr/index.ts index 2bf3c0549e..54f0b7ffb5 100644 --- a/src/extension/providers/nostr/index.ts +++ b/src/extension/providers/nostr/index.ts @@ -79,17 +79,13 @@ class Nip44 { this.provider = provider; } - async encrypt(peer: string, plaintext: string, v: number) { + async encrypt(pairs: [string, string][]) { await this.provider.enable(); - return this.provider.execute("nip44EncryptOrPrompt", { - peer, - plaintext, - v, - }); + return this.provider.execute("nip44EncryptOrPrompt", { pairs }); } - async decrypt(peer: string, payload: string) { + async decrypt(pairs: [string, string][]) { await this.provider.enable(); - return this.provider.execute("nip44DecryptOrPrompt", { peer, payload }); + return this.provider.execute("nip44DecryptOrPrompt", { pairs }); } } diff --git a/src/types.ts b/src/types.ts index 7eb17c16df..16239852c5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -175,6 +175,10 @@ export type NavigationState = { message: string; }; + nip44Encrypt: { + pairs: [string, string][]; + }; + psbt?: string; requestPermission: { method: string; @@ -562,16 +566,14 @@ export interface MessageDecryptGet extends MessageDefault { export interface MessageNip44EncryptGet extends MessageDefault { args: { - peer: string; - plaintext: string; + pairs: [string, string][]; }; action: "encrypt"; } export interface MessageNip44DecryptGet extends MessageDefault { args: { - peer: string; - payload: string; + pairs: [string, string][]; }; action: "decrypt"; }