Skip to content

Commit

Permalink
Use separate encrypt/decrypt dialogs for nip44
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Staab committed Jan 9, 2024
1 parent ba03a79 commit 20977f5
Show file tree
Hide file tree
Showing 10 changed files with 280 additions and 59 deletions.
10 changes: 10 additions & 0 deletions src/app/router/Prompt/Prompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -132,6 +134,14 @@ function Prompt() {
path="public/nostr/confirmDecrypt"
element={<NostrConfirmDecrypt />}
/>
<Route
path="public/nostr/confirmNip44Encrypt"
element={<NostrConfirmNip44Encrypt />}
/>
<Route
path="public/nostr/confirmNip44Decrypt"
element={<NostrConfirmNip44Decrypt />}
/>
<Route
path="public/nostr/confirmGetPublicKey"
element={<NostrConfirmGetPublicKey />}
Expand Down
113 changes: 113 additions & 0 deletions src/app/screens/Nostr/ConfirmNip44Decrypt.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLAnchorElement>) {
event.preventDefault();
msg.error(USER_REJECTED_ERROR);
}

async function block(event: React.MouseEvent<HTMLAnchorElement>) {
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<HTMLFormElement>) {
event.preventDefault();
confirm();
}

return (
<div className="h-full flex flex-col overflow-y-auto no-scrollbar">
<ScreenHeader title={t("title")} />
<form onSubmit={handleSubmit} className="h-full">
<Container justifyBetween maxWidth="sm">
<PublisherCard
title={origin.name}
image={origin.icon}
url={origin.host}
isSmall={false}
/>
<div className="dark:text-white pt-6 mb-4">
<p className="mb-2">{t("allow", { host: origin.host })}</p>
<p className="dark:text-white">
<CheckIcon className="w-5 h-5 mr-2 inline" />
{tPermissions("nostr.nip44decrypt")}
</p>
</div>

<div className="text-center flex flex-col">
<div className="flex items-center mb-4">
<Checkbox
id="remember_permission"
name="remember_permission"
checked={rememberPermission}
onChange={(event) => {
setRememberPermission(event.target.checked);
}}
/>
<label
htmlFor="remember_permission"
className="cursor-pointer ml-2 block text-sm text-gray-900 font-medium dark:text-white"
>
{tCommon("actions.remember")}
</label>
</div>
<ConfirmOrCancel
disabled={loading}
loading={loading}
label={tCommon("actions.confirm")}
onCancel={reject}
/>
<a
className="mt-4 underline text-sm text-gray-400 overflow-hidden text-ellipsis whitespace-nowrap"
href="#"
onClick={block}
>
{t("block_and_ignore", { host: origin.host })}
</a>
</div>
</Container>
</form>
</div>
);
}

export default NostrConfirmNip44Decrypt;
121 changes: 121 additions & 0 deletions src/app/screens/Nostr/ConfirmNip44Encrypt.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLAnchorElement>) {
event.preventDefault();
msg.error(USER_REJECTED_ERROR);
}

async function block(event: React.MouseEvent<HTMLAnchorElement>) {
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<HTMLFormElement>) {
event.preventDefault();
confirm();
}

return (
<div className="h-full flex flex-col overflow-y-auto no-scrollbar">
<ScreenHeader title={t("title")} />
<form onSubmit={handleSubmit} className="h-full">
<Container justifyBetween maxWidth="sm">
<div>
<PublisherCard
title={origin.name}
image={origin.icon}
url={origin.host}
isSmall={true}
/>
{pairs?.map(([pubkey, text]) => (
<>
<ContentMessage
heading={t("allow_encrypt", {
host: origin.host,
})}
content={text}
/>
<div className="whitespace-pre-wrap break-words p-2 mb-4 shadow bg-white rounded-lg dark:bg-surface-02dp text-gray-500 dark:text-gray-400">
{t("recipient")}: {nostr.hexToNip19(pubkey, "npub")}
</div>
</>
))}
</div>
<div className="text-center flex flex-col">
<div className="flex items-center mb-4">
<Checkbox
id="remember_permission"
name="remember_permission"
checked={rememberPermission}
onChange={(event) => {
setRememberPermission(event.target.checked);
}}
/>
<label
htmlFor="remember_permission"
className="cursor-pointer ml-2 block text-sm text-gray-900 font-medium dark:text-white"
>
{tCommon("actions.remember")}
</label>
</div>
<ConfirmOrCancel
disabled={loading}
loading={loading}
label={tCommon("actions.confirm")}
onCancel={reject}
/>
<a
className="mt-4 underline text-sm text-gray-400 overflow-hidden text-ellipsis whitespace-nowrap"
href="#"
onClick={block}
>
{t("block_and_ignore", { host: origin.host })}
</a>
</div>
</Container>
</form>
</div>
);
}

export default NostrConfirmNip44Encrypt;
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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 {
Expand All @@ -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
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -22,24 +19,18 @@ 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<{
confirm: boolean;
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,
},
},
});
Expand All @@ -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 {
Expand Down
8 changes: 4 additions & 4 deletions src/extension/background-script/nostr/__test__/nostr.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand All @@ -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";
}
Expand Down
Loading

0 comments on commit 20977f5

Please sign in to comment.