From f24eade33718706e20d6a333d19601a5fa8acb62 Mon Sep 17 00:00:00 2001 From: Jonathan Staab Date: Thu, 10 Aug 2023 14:30:32 -0700 Subject: [PATCH 1/8] feat: add NIP 44 encryption support --- package.json | 1 + src/common/utils/lruCache.ts | 33 ++++++++ .../actions/nostr/nip44DecryptOrPrompt.ts | 75 +++++++++++++++++++ .../actions/nostr/nip44EncryptOrPrompt.ts | 74 ++++++++++++++++++ .../nostr/__test__/nostr.test.ts | 35 ++++++++- .../background-script/nostr/index.ts | 27 ++++++- src/extension/providers/nostr/index.ts | 23 ++++++ src/i18n/locales/cs/translation.json | 3 + src/i18n/locales/da/translation.json | 3 + src/i18n/locales/de/translation.json | 3 + src/i18n/locales/en/translation.json | 13 ++-- src/i18n/locales/es/translation.json | 7 +- src/i18n/locales/fa/translation.json | 3 + src/i18n/locales/fr/translation.json | 3 + src/i18n/locales/hi/translation.json | 5 +- src/i18n/locales/id/translation.json | 3 + src/i18n/locales/it/translation.json | 5 +- src/i18n/locales/mr/translation.json | 3 + src/i18n/locales/pl/translation.json | 3 + src/i18n/locales/pt_BR/translation.json | 7 +- src/i18n/locales/ru/translation.json | 3 + src/i18n/locales/sl/translation.json | 7 +- src/i18n/locales/sv/translation.json | 5 +- src/i18n/locales/th/translation.json | 2 + src/i18n/locales/uk/translation.json | 3 + src/i18n/locales/zh_Hans/translation.json | 7 +- src/i18n/locales/zh_Hant/translation.json | 3 + src/types.ts | 18 +++++ 28 files changed, 357 insertions(+), 20 deletions(-) create mode 100644 src/common/utils/lruCache.ts create mode 100644 src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts create mode 100644 src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts diff --git a/package.json b/package.json index 181fc4e34d..6c42a3cf32 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "lodash.merge": "^4.6.2", "lodash.pick": "^4.4.0", "lodash.snakecase": "^4.1.1", + "nostr-tools": "^2.1.0", "pubsub-js": "^1.9.4", "react": "^18.2.0", "react-confetti": "^6.1.0", diff --git a/src/common/utils/lruCache.ts b/src/common/utils/lruCache.ts new file mode 100644 index 0000000000..1cd96052cd --- /dev/null +++ b/src/common/utils/lruCache.ts @@ -0,0 +1,33 @@ +export class LRUCache { + map = new Map(); + keys: T[] = []; + + constructor(readonly maxSize: number) {} + + has(k: T) { + return this.map.has(k); + } + + get(k: T) { + const v = this.map.get(k); + + if (v !== undefined) { + this.keys.push(k as T); + + if (this.keys.length > this.maxSize * 2) { + this.keys.splice(-this.maxSize); + } + } + + return v; + } + + set(k: T, v: U) { + this.map.set(k, v); + this.keys.push(k); + + if (this.map.size > this.maxSize) { + this.map.delete(this.keys.shift() as T); + } + } +} diff --git a/src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts b/src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts new file mode 100644 index 0000000000..186ffc37c6 --- /dev/null +++ b/src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts @@ -0,0 +1,75 @@ +import { USER_REJECTED_ERROR } from "~/common/constants"; +import utils from "~/common/lib/utils"; +import { getHostFromSender } from "~/common/utils/helpers"; +import { + addPermissionFor, + hasPermissionFor, +} from "~/extension/background-script/permissions"; +import state from "~/extension/background-script/state"; +import { MessageNip44DecryptGet, PermissionMethodNostr, Sender } from "~/types"; + +const nip44DecryptOrPrompt = async ( + message: MessageNip44DecryptGet, + sender: Sender +) => { + const host = getHostFromSender(sender); + if (!host) return; + + try { + const hasPermission = await hasPermissionFor( + PermissionMethodNostr["NOSTR_NIP44DECRYPT"], + host + ); + + if (hasPermission) { + const nostr = await state.getState().getNostr(); + const response = await nostr.nip44Decrypt( + message.args.peer, + message.args.payload + ); + + return { data: response }; + } else { + const promptResponse = await utils.openPrompt<{ + confirm: boolean; + rememberPermission: boolean; + }>({ + ...message, + action: "public/nostr/confirmEncryptOrDecrypt", + args: { + encryptOrDecrypt: { + action: "decrypt", + peer: message.args.peer, + payload: message.args.payload, + }, + }, + }); + + // add permission to db only if user decided to always allow this request + if (promptResponse.data.rememberPermission) { + await addPermissionFor( + PermissionMethodNostr["NOSTR_NIP44DECRYPT"], + host + ); + } + if (promptResponse.data.confirm) { + const nostr = await state.getState().getNostr(); + const response = await nostr.nip44Decrypt( + message.args.peer, + message.args.payload + ); + + return { data: response }; + } else { + return { error: USER_REJECTED_ERROR }; + } + } + } catch (e) { + console.error("decrypt failed", e); + if (e instanceof Error) { + return { error: e.message }; + } + } +}; + +export default nip44DecryptOrPrompt; diff --git a/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts b/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts new file mode 100644 index 0000000000..2a0804aebd --- /dev/null +++ b/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts @@ -0,0 +1,74 @@ +import { USER_REJECTED_ERROR } from "~/common/constants"; +import utils from "~/common/lib/utils"; +import { getHostFromSender } from "~/common/utils/helpers"; +import { + addPermissionFor, + hasPermissionFor, +} from "~/extension/background-script/permissions"; +import state from "~/extension/background-script/state"; +import { MessageNip44EncryptGet, PermissionMethodNostr, Sender } from "~/types"; + +const nip44EncryptOrPrompt = async ( + message: MessageNip44EncryptGet, + sender: Sender +) => { + const host = getHostFromSender(sender); + if (!host) return; + + try { + const hasPermission = await hasPermissionFor( + PermissionMethodNostr["NOSTR_NIP44ENCRYPT"], + host + ); + + if (hasPermission) { + const response = (await state.getState().getNostr()).nip44Encrypt( + message.args.peer, + message.args.plaintext, + message.args.v + ); + return { data: response }; + } else { + const promptResponse = await utils.openPrompt<{ + confirm: boolean; + rememberPermission: boolean; + }>({ + ...message, + action: "public/nostr/confirmEncryptOrDecrypt", + args: { + encryptOrDecrypt: { + action: "encrypt", + peer: message.args.peer, + message: message.args.plaintext, + }, + }, + }); + + // add permission to db only if user decided to always allow this request + if (promptResponse.data.rememberPermission) { + await addPermissionFor( + PermissionMethodNostr["NOSTR_NIP44ENCRYPT"], + host + ); + } + if (promptResponse.data.confirm) { + const response = (await state.getState().getNostr()).nip44Encrypt( + message.args.peer, + message.args.plaintext, + message.args.v + ); + + return { data: response }; + } else { + return { error: USER_REJECTED_ERROR }; + } + } + } catch (e) { + console.error("encrypt failed", e); + if (e instanceof Error) { + return { error: e.message }; + } + } +}; + +export default nip44EncryptOrPrompt; diff --git a/src/extension/background-script/nostr/__test__/nostr.test.ts b/src/extension/background-script/nostr/__test__/nostr.test.ts index 779fd65007..3db5956d9a 100644 --- a/src/extension/background-script/nostr/__test__/nostr.test.ts +++ b/src/extension/background-script/nostr/__test__/nostr.test.ts @@ -18,7 +18,7 @@ const carol = { publicKey: "a8c7d70a7d2e2826ce519a0a490fb953464c9d130235c321282983cd73be333f", }; -describe("nostr", () => { +describe("nostr.nip04", () => { test("encrypt & decrypt", async () => { const aliceNostr = new Nostr(alice.privateKey); @@ -50,3 +50,36 @@ describe("nostr", () => { expect(decrypted).not.toMatch(message); }); }); + +describe("nostr.nip44", () => { + test("encrypt & decrypt", async () => { + 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 bobNostr = new Nostr(bob.privateKey); + + const decrypted = await bobNostr.nip44Decrypt(alice.publicKey, encrypted); + + expect(decrypted).toMatch(message); + }); + + test("Carol can't decrypt Alice's message for Bob", async () => { + 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 carolNostr = new Nostr(carol.privateKey); + + let decrypted; + try { + decrypted = await carolNostr.nip44Decrypt(alice.publicKey, encrypted); + } catch (e) { + decrypted = "error decrypting message"; + } + + expect(decrypted).not.toMatch(message); + }); +}); diff --git a/src/extension/background-script/nostr/index.ts b/src/extension/background-script/nostr/index.ts index ef468791b9..4e6c9d8082 100644 --- a/src/extension/background-script/nostr/index.ts +++ b/src/extension/background-script/nostr/index.ts @@ -1,20 +1,33 @@ import { schnorr } from "@noble/curves/secp256k1"; import * as secp256k1 from "@noble/secp256k1"; +import { nip44 } from "nostr-tools"; import { Buffer } from "buffer"; import * as CryptoJS from "crypto-js"; import { AES } from "crypto-js"; import Base64 from "crypto-js/enc-base64"; import Hex from "crypto-js/enc-hex"; import Utf8 from "crypto-js/enc-utf8"; +import { LRUCache } from "~/common/utils/lruCache"; import { Event } from "~/extension/providers/nostr/types"; import { getEventHash, signEvent } from "../actions/nostr/helpers"; class Nostr { - privateKey: string; + nip44SharedSecretCache = new LRUCache(100); - constructor(privateKey: string) { - this.privateKey = privateKey; + constructor(readonly privateKey: string) {} + + // Deriving shared secret is an expensive computation + getNip44SharedSecret(pk: string) { + let key = this.nip44SharedSecretCache.get(pk); + + if (!key) { + key = nip44.v2.utils.getConversationKey(this.privateKey, pk); + + this.nip44SharedSecretCache.set(pk, key); + } + + return key; } getPublicKey() { @@ -69,6 +82,14 @@ class Nostr { return Utf8.stringify(decrypted); } + nip44Encrypt(pubkey: string, text: string) { + return nip44.v2.encrypt(text, this.getNip44SharedSecret(pubkey)); + } + + nip44Decrypt(pubkey: string, payload: string) { + return nip44.v2.decrypt(payload, this.getNip44SharedSecret(pubkey)); + } + getEventHash(event: Event) { return getEventHash(event); } diff --git a/src/extension/providers/nostr/index.ts b/src/extension/providers/nostr/index.ts index 2570a8a01f..2bf3c0549e 100644 --- a/src/extension/providers/nostr/index.ts +++ b/src/extension/providers/nostr/index.ts @@ -10,6 +10,7 @@ declare global { export default class NostrProvider extends ProviderBase { nip04 = new Nip04(this); + nip44 = new Nip44(this); constructor() { super("nostr"); @@ -70,3 +71,25 @@ class Nip04 { }); } } + +class Nip44 { + provider: NostrProvider; + + constructor(provider: NostrProvider) { + this.provider = provider; + } + + async encrypt(peer: string, plaintext: string, v: number) { + await this.provider.enable(); + return this.provider.execute("nip44EncryptOrPrompt", { + peer, + plaintext, + v, + }); + } + + async decrypt(peer: string, payload: string) { + await this.provider.enable(); + return this.provider.execute("nip44DecryptOrPrompt", { peer, payload }); + } +} diff --git a/src/i18n/locales/cs/translation.json b/src/i18n/locales/cs/translation.json index ffc48587e5..4a36035924 100644 --- a/src/i18n/locales/cs/translation.json +++ b/src/i18n/locales/cs/translation.json @@ -831,6 +831,9 @@ "nostr": { "getpublickey": "Přečíst svůj veřejný klíč", "nip04decrypt": "Dešifrovat data", + "nip04encrypt": "Zašifrovat data", + "nip44decrypt": "Dešifrovat data", + "nip44encrypt": "Zašifrovat data", "signmessage": "Podepsat zprávu klíčem" }, "lnc": { diff --git a/src/i18n/locales/da/translation.json b/src/i18n/locales/da/translation.json index 8ca85a3d39..8caa6623a7 100644 --- a/src/i18n/locales/da/translation.json +++ b/src/i18n/locales/da/translation.json @@ -699,6 +699,9 @@ "nostr": { "getpublickey": "Læs din offentlige nøgle", "nip04decrypt": "De-krypter data", + "nip04encrypt": "Krypter data", + "nip44decrypt": "De-krypter data", + "nip44encrypt": "Krypter data", "signmessage": "Underskriv meddelelse med din nøgle" }, "commando": { diff --git a/src/i18n/locales/de/translation.json b/src/i18n/locales/de/translation.json index e5ed615d55..3f4fae15aa 100644 --- a/src/i18n/locales/de/translation.json +++ b/src/i18n/locales/de/translation.json @@ -1214,6 +1214,9 @@ "nostr": { "getpublickey": "Lese deinen öffentlichen Schlüssel", "nip04decrypt": "Daten entschlüsseln", + "nip04encrypt": "Daten verschlüsseln", + "nip44decrypt": "Daten entschlüsseln", + "nip44encrypt": "Daten verschlüsseln", "signmessage": "Unterschreibe deine Nachricht mit deinem Schlüssel" }, "lnc": { diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 19c7ed1e4d..572307d51a 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -1167,11 +1167,6 @@ "webln": { "getbalance": "Read the balance of your account" }, - "nostr": { - "getpublickey": "Read your public key", - "signmessage": "Sign message with your key", - "nip04decrypt": "Decrypt data" - }, "bitcoin": { "getaddress": "Read your Bitcoin receive address" }, @@ -1179,6 +1174,14 @@ "getaddress": "Read your Liquid receive address", "signschnorr": "Sign message with your key" }, + "nostr": { + "getpublickey": "Read your public key.", + "nip04encrypt": "Encrypt data.", + "nip04decrypt": "Decrypt data.", + "nip44encrypt": "Encrypt data.", + "nip44decrypt": "Decrypt data.", + "signmessage": "Sign message with your key." + }, "commando": { "bkpr-listbalances": "List of all current and historical account balances", "checkmessage": "Verify that the signature was generated by a given node", diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index 2d42f47f87..3e2a6f797a 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -846,9 +846,12 @@ "feerates": "Devuelve las tarifas que utilizará CLN" }, "nostr": { - "signmessage": "Firma el mensaje con tu clave", "getpublickey": "Leer tu clave pública", - "nip04decrypt": "Descifrar datos" + "nip04decrypt": "Descifrar datos", + "nip04encrypt": "Cifrar datos", + "nip44decrypt": "Descifrar datos", + "nip44encrypt": "Cifrar datos", + "signmessage": "Firma el mensaje con tu clave" }, "lnd": { "queryroutes": "Consultar una posible ruta", diff --git a/src/i18n/locales/fa/translation.json b/src/i18n/locales/fa/translation.json index 77b9f48533..424cfd0f1e 100644 --- a/src/i18n/locales/fa/translation.json +++ b/src/i18n/locales/fa/translation.json @@ -984,6 +984,9 @@ "nostr": { "getpublickey": "کلید عمومی تان را بخوانید", "nip04decrypt": "رمزگشایی داده", + "nip04encrypt": "رمزنگاری داده", + "nip44decrypt": "رمزگشایی داده", + "nip44encrypt": "رمزنگاری داده", "signmessage": "پیام را با کلید خود امضا کنید" }, "commando": { diff --git a/src/i18n/locales/fr/translation.json b/src/i18n/locales/fr/translation.json index 66378ae9e5..b77a41eeba 100644 --- a/src/i18n/locales/fr/translation.json +++ b/src/i18n/locales/fr/translation.json @@ -705,6 +705,9 @@ "nostr": { "getpublickey": "Lisez votre clé publique", "nip04decrypt": "Déchiffrer les données", + "nip04encrypt": "Crypter les données", + "nip44decrypt": "Déchiffrer les données", + "nip44encrypt": "Crypter les données", "signmessage": "Signez le message avec votre clé" }, "commando": { diff --git a/src/i18n/locales/hi/translation.json b/src/i18n/locales/hi/translation.json index bf6fafbf7d..b76e435dc6 100644 --- a/src/i18n/locales/hi/translation.json +++ b/src/i18n/locales/hi/translation.json @@ -1151,7 +1151,10 @@ "permissions": { "nostr": { "getpublickey": "अपनी सार्वजनिक कुंजी पढ़ें", - "nip04decrypt": "डेटा डिक्रिप्ट करें", + "nip04decrypt": "डेटा डिक्रिप्ट करे", + "nip04encrypt": "डेटा एन्क्रिप्ट करे", + "nip44decrypt": "डेटा डिक्रिप्ट करे", + "nip44encrypt": "डेटा एन्क्रिप्ट करे", "signmessage": "अपनी चाबी से संदेश पर हस्ताक्षर करें" }, "commando": { diff --git a/src/i18n/locales/id/translation.json b/src/i18n/locales/id/translation.json index dd447b1ad1..a3494dda10 100644 --- a/src/i18n/locales/id/translation.json +++ b/src/i18n/locales/id/translation.json @@ -797,6 +797,9 @@ "permissions": { "nostr": { "nip04decrypt": "Dekripsi data", + "nip04encrypt": "Enkripsi data", + "nip44decrypt": "Dekripsi data", + "nip44encrypt": "Enkripsi data", "getpublickey": "Membaca kunci publik Anda" }, "commando": { diff --git a/src/i18n/locales/it/translation.json b/src/i18n/locales/it/translation.json index 9796906b65..46cbb63dd0 100644 --- a/src/i18n/locales/it/translation.json +++ b/src/i18n/locales/it/translation.json @@ -774,8 +774,11 @@ "addinvoice": "Crea nuove ricevute di pagamento" }, "nostr": { - "nip04decrypt": "Decripta i dati", "getpublickey": "Leggi la tua chiave pubblica", + "nip04decrypt": "Decripta i dati", + "nip04encrypt": "Cripta i dati", + "nip44decrypt": "Decripta i dati", + "nip44encrypt": "Cripta i dati", "signmessage": "Firma il messaggio con la tua chiave" } } diff --git a/src/i18n/locales/mr/translation.json b/src/i18n/locales/mr/translation.json index 76b044c582..7ca63cc513 100644 --- a/src/i18n/locales/mr/translation.json +++ b/src/i18n/locales/mr/translation.json @@ -721,6 +721,9 @@ "nostr": { "getpublickey": "तुमची सार्वजनिक की वाचा", "nip04decrypt": "डेटा Decrypt करा", + "nip04encrypt": "डेटा Encrypt करा", + "nip44decrypt": "डेटा Decrypt करा", + "nip44encrypt": "डेटा Encrypt करा", "signmessage": "तुमच्या किल्लीने संदेशावर स्वाक्षरी करा" }, "commando": { diff --git a/src/i18n/locales/pl/translation.json b/src/i18n/locales/pl/translation.json index 6ca968e523..bd2da331e0 100644 --- a/src/i18n/locales/pl/translation.json +++ b/src/i18n/locales/pl/translation.json @@ -851,6 +851,9 @@ "nostr": { "getpublickey": "Czytanie Twojego klucza publicznego.", "nip04decrypt": "Deszyfrowanie danych.", + "nip04encrypt": "Szyfrowanie danych.", + "nip44decrypt": "Deszyfrowanie danych.", + "nip44encrypt": "Szyfrowanie danych.", "signmessage": "Podpisywanie wiadomości Twoim kluczem." }, "commando": { diff --git a/src/i18n/locales/pt_BR/translation.json b/src/i18n/locales/pt_BR/translation.json index f02aacbdf0..9c49be4ced 100644 --- a/src/i18n/locales/pt_BR/translation.json +++ b/src/i18n/locales/pt_BR/translation.json @@ -1104,8 +1104,11 @@ "permissions": { "nostr": { "getpublickey": "Ler sua chave pública", - "signmessage": "Assinar mensagem com sua chave", - "nip04decrypt": "Descriptografar dados" + "nip04decrypt": "Descriptografar dados", + "nip04encrypt": "Criptografar dados", + "nip44decrypt": "Descriptografar dados", + "nip44encrypt": "Criptografar dados", + "signmessage": "Assinar mensagem com sua chave" }, "lnc": { "openchannel": "" diff --git a/src/i18n/locales/ru/translation.json b/src/i18n/locales/ru/translation.json index 773563deba..0fb67ef3b7 100644 --- a/src/i18n/locales/ru/translation.json +++ b/src/i18n/locales/ru/translation.json @@ -709,6 +709,9 @@ "nostr": { "getpublickey": "", "nip04decrypt": "", + "nip04encrypt": "", + "nip44decrypt": "", + "nip44encrypt": "", "signmessage": "" }, "commando": { diff --git a/src/i18n/locales/sl/translation.json b/src/i18n/locales/sl/translation.json index 01be28d13b..baf2ae7b6b 100644 --- a/src/i18n/locales/sl/translation.json +++ b/src/i18n/locales/sl/translation.json @@ -630,9 +630,12 @@ "getaddress": "Bere tvoj Liquid naslov za prejemanje" }, "nostr": { - "signmessage": "Podpiši sporočilo s svojim ključem", + "getpublickey": "Bere tvoj javni ključ", "nip04decrypt": "Dekriptiraj podatke", - "getpublickey": "Bere tvoj javni ključ" + "nip04encrypt": "Kriptiraj podatke", + "nip44decrypt": "Dekriptiraj podatke", + "nip44encrypt": "Kriptiraj podatke", + "signmessage": "Podpiši sporočilo s svojim ključem" }, "webln": { "getbalance": "Bere stanje tvojega računa" diff --git a/src/i18n/locales/sv/translation.json b/src/i18n/locales/sv/translation.json index 860b3b23e2..06113443db 100644 --- a/src/i18n/locales/sv/translation.json +++ b/src/i18n/locales/sv/translation.json @@ -1079,8 +1079,11 @@ }, "permissions": { "nostr": { - "nip04decrypt": "Dekryptera data", "getpublickey": "Läs din publika nyckel", + "nip04decrypt": "Dekryptera data", + "nip04encrypt": "Kryptera data", + "nip44decrypt": "Dekryptera data", + "nip44encrypt": "Kryptera data", "signmessage": "Skriv under meddelandet med din nyckel" }, "lnd": { diff --git a/src/i18n/locales/th/translation.json b/src/i18n/locales/th/translation.json index 78f00e57b0..63fb8ca534 100644 --- a/src/i18n/locales/th/translation.json +++ b/src/i18n/locales/th/translation.json @@ -772,6 +772,8 @@ "nostr": { "signmessage": "เซ็นข้อความด้วย key ของคุณ", "nip04decrypt": "ถอดรหัสข้อมูล", + "nip44encrypt": "เข้ารหัสข้อมูล", + "nip44decrypt": "ถอดรหัสข้อมูล", "getpublickey": "แสดง public key ของคุณ" }, "commando": { diff --git a/src/i18n/locales/uk/translation.json b/src/i18n/locales/uk/translation.json index 2df940cbca..e581cfa736 100644 --- a/src/i18n/locales/uk/translation.json +++ b/src/i18n/locales/uk/translation.json @@ -699,6 +699,9 @@ "nostr": { "getpublickey": "", "nip04decrypt": "", + "nip04encrypt": "", + "nip44decrypt": "", + "nip44encrypt": "", "signmessage": "" }, "commando": { diff --git a/src/i18n/locales/zh_Hans/translation.json b/src/i18n/locales/zh_Hans/translation.json index 4b7ce4737a..b59f0c1bb9 100644 --- a/src/i18n/locales/zh_Hans/translation.json +++ b/src/i18n/locales/zh_Hans/translation.json @@ -972,9 +972,12 @@ }, "permissions": { "nostr": { - "signmessage": "使用你的密钥签署消息", - "nip04decrypt": "解密数据", "getpublickey": "读取你的公钥" + "nip04decrypt": "解密数据", + "nip04encrypt": "加密数据", + "nip44decrypt": "解密数据", + "nip44encrypt": "加密数据", + "signmessage": "使用你的密钥签署消息", }, "commando": { "bkpr-listbalances": "所有当前和历史帐户余额列表", diff --git a/src/i18n/locales/zh_Hant/translation.json b/src/i18n/locales/zh_Hant/translation.json index 53dde1b6c5..8a3d05375e 100644 --- a/src/i18n/locales/zh_Hant/translation.json +++ b/src/i18n/locales/zh_Hant/translation.json @@ -981,6 +981,9 @@ "nostr": { "getpublickey": "讀取你的公鑰", "nip04decrypt": "解密數據", + "nip04encrypt": "加密數據", + "nip44decrypt": "解密數據", + "nip44encrypt": "加密數據", "signmessage": "用你的密鑰簽署消息" }, "commando": { diff --git a/src/types.ts b/src/types.ts index 6bc4f7f414..7857c9c1ab 100644 --- a/src/types.ts +++ b/src/types.ts @@ -560,6 +560,22 @@ export interface MessageDecryptGet extends MessageDefault { action: "decrypt"; } +export interface MessageNip44EncryptGet extends MessageDefault { + args: { + peer: string; + plaintext: string; + }; + action: "encrypt"; +} + +export interface MessageNip44DecryptGet extends MessageDefault { + args: { + peer: string; + payload: string; + }; + action: "decrypt"; +} + export interface MessageSignPsbt extends MessageDefault { args: { psbt: string; @@ -769,6 +785,8 @@ export enum PermissionMethodNostr { NOSTR_GETPUBLICKEY = "nostr/getPublicKey", NOSTR_NIP04DECRYPT = "nostr/nip04decrypt", NOSTR_NIP04ENCRYPT = "nostr/nip04encrypt", + NOSTR_NIP44DECRYPT = "nostr/nip44decrypt", + NOSTR_NIP44ENCRYPT = "nostr/nip44encrypt", } export interface DbPermission { From f3bb66d4980def01df5f8cf78e5a218eb55e3fb1 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Tue, 9 Jan 2024 13:06:29 -0800 Subject: [PATCH 2/8] Use separate encrypt/decrypt dialogs for nip44 --- src/app/router/Prompt/Prompt.tsx | 10 ++ src/app/screens/Nostr/ConfirmNip44Decrypt.tsx | 112 ++++++++++++++++ src/app/screens/Nostr/ConfirmNip44Encrypt.tsx | 121 ++++++++++++++++++ .../actions/nostr/nip44DecryptOrPrompt.ts | 19 +-- .../actions/nostr/nip44EncryptOrPrompt.ts | 16 +-- .../nostr/__test__/nostr.test.ts | 12 +- .../background-script/nostr/index.ts | 12 +- src/extension/content-script/nostr.js | 2 + src/extension/providers/nostr/index.ts | 12 +- src/i18n/locales/zh_Hans/translation.json | 4 +- src/types.ts | 10 +- 11 files changed, 281 insertions(+), 49 deletions(-) create mode 100644 src/app/screens/Nostr/ConfirmNip44Decrypt.tsx create mode 100644 src/app/screens/Nostr/ConfirmNip44Encrypt.tsx 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..7e99b8323c --- /dev/null +++ b/src/app/screens/Nostr/ConfirmNip44Decrypt.tsx @@ -0,0 +1,112 @@ +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..2ab8ceb9ec 100644 --- a/src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts @@ -23,10 +23,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 +32,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 +44,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..2f59dd11a0 100644 --- a/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts @@ -23,9 +23,7 @@ const nip44EncryptOrPrompt = async ( if (hasPermission) { const response = (await state.getState().getNostr()).nip44Encrypt( - message.args.peer, - message.args.plaintext, - message.args.v + message.args.pairs ); return { data: response }; } else { @@ -34,12 +32,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, }, }, }); @@ -53,9 +49,7 @@ const nip44EncryptOrPrompt = async ( } if (promptResponse.data.confirm) { const response = (await state.getState().getNostr()).nip44Encrypt( - message.args.peer, - message.args.plaintext, - message.args.v + message.args.pairs ); return { data: response }; diff --git a/src/extension/background-script/nostr/__test__/nostr.test.ts b/src/extension/background-script/nostr/__test__/nostr.test.ts index 3db5956d9a..88fe9ef18f 100644 --- a/src/extension/background-script/nostr/__test__/nostr.test.ts +++ b/src/extension/background-script/nostr/__test__/nostr.test.ts @@ -56,11 +56,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 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 +71,15 @@ 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..247ad27f41 100644 --- a/src/extension/background-script/nostr/index.ts +++ b/src/extension/background-script/nostr/index.ts @@ -82,12 +82,16 @@ 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/i18n/locales/zh_Hans/translation.json b/src/i18n/locales/zh_Hans/translation.json index b59f0c1bb9..5a3deb98ec 100644 --- a/src/i18n/locales/zh_Hans/translation.json +++ b/src/i18n/locales/zh_Hans/translation.json @@ -972,12 +972,12 @@ }, "permissions": { "nostr": { - "getpublickey": "读取你的公钥" + "getpublickey": "读取你的公钥", "nip04decrypt": "解密数据", "nip04encrypt": "加密数据", "nip44decrypt": "解密数据", "nip44encrypt": "加密数据", - "signmessage": "使用你的密钥签署消息", + "signmessage": "使用你的密钥签署消息" }, "commando": { "bkpr-listbalances": "所有当前和历史帐户余额列表", diff --git a/src/types.ts b/src/types.ts index 7857c9c1ab..779603cfdd 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"; } From 9318232b6be229e9f2f29e948d64435dd81738c0 Mon Sep 17 00:00:00 2001 From: pavanjoshi914 Date: Thu, 8 Feb 2024 16:00:31 +0530 Subject: [PATCH 3/8] fix: make functionality work --- src/extension/background-script/actions/nostr/index.ts | 4 ++++ src/extension/background-script/router.ts | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/extension/background-script/actions/nostr/index.ts b/src/extension/background-script/actions/nostr/index.ts index 32fb85eee4..c1db56ac37 100644 --- a/src/extension/background-script/actions/nostr/index.ts +++ b/src/extension/background-script/actions/nostr/index.ts @@ -8,6 +8,8 @@ import getPrivateKey from "./getPrivateKey"; import getPublicKeyOrPrompt from "./getPublicKeyOrPrompt"; import getRelays from "./getRelays"; import isEnabled from "./isEnabled"; +import nip44DecryptOrPrompt from "./nip44DecryptOrPrompt"; +import nip44EncryptOrPrompt from "./nip44EncryptOrPrompt"; import removePrivateKey from "./removePrivateKey"; import setPrivateKey from "./setPrivateKey"; import signEventOrPrompt from "./signEventOrPrompt"; @@ -23,6 +25,8 @@ export { getPublicKeyOrPrompt, getRelays, isEnabled, + nip44DecryptOrPrompt, + nip44EncryptOrPrompt, removePrivateKey, setPrivateKey, signEventOrPrompt, diff --git a/src/extension/background-script/router.ts b/src/extension/background-script/router.ts index 688d017536..2378b6de4a 100644 --- a/src/extension/background-script/router.ts +++ b/src/extension/background-script/router.ts @@ -128,6 +128,8 @@ const routes = { getRelays: nostr.getRelays, encryptOrPrompt: nostr.encryptOrPrompt, decryptOrPrompt: nostr.decryptOrPrompt, + nip44EncryptOrPrompt: nostr.nip44EncryptOrPrompt, + nip44DecryptOrPrompt: nostr.nip44DecryptOrPrompt, }, }, }; From 51538c381b9b7aa8656f20d5a22e8a297285bbd8 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Tue, 20 Feb 2024 13:53:22 -0800 Subject: [PATCH 4/8] Switch back to simple interface for nip44 --- src/app/router/Prompt/Prompt.tsx | 10 -- src/app/screens/Nostr/ConfirmNip44Decrypt.tsx | 112 ---------------- src/app/screens/Nostr/ConfirmNip44Encrypt.tsx | 121 ------------------ .../actions/nostr/nip44DecryptOrPrompt.ts | 12 +- .../actions/nostr/nip44EncryptOrPrompt.ts | 12 +- .../nostr/__test__/nostr.test.ts | 12 +- .../background-script/nostr/index.ts | 12 +- src/extension/providers/nostr/index.ts | 14 +- src/types.ts | 9 +- 9 files changed, 41 insertions(+), 273 deletions(-) delete mode 100644 src/app/screens/Nostr/ConfirmNip44Decrypt.tsx delete mode 100644 src/app/screens/Nostr/ConfirmNip44Encrypt.tsx diff --git a/src/app/router/Prompt/Prompt.tsx b/src/app/router/Prompt/Prompt.tsx index da9a42c5fe..090d637b58 100644 --- a/src/app/router/Prompt/Prompt.tsx +++ b/src/app/router/Prompt/Prompt.tsx @@ -30,8 +30,6 @@ 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. @@ -134,14 +132,6 @@ function Prompt() { path="public/nostr/confirmDecrypt" element={} /> - } - /> - } - /> } diff --git a/src/app/screens/Nostr/ConfirmNip44Decrypt.tsx b/src/app/screens/Nostr/ConfirmNip44Decrypt.tsx deleted file mode 100644 index 7e99b8323c..0000000000 --- a/src/app/screens/Nostr/ConfirmNip44Decrypt.tsx +++ /dev/null @@ -1,112 +0,0 @@ -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 deleted file mode 100644 index 09c4b4ede9..0000000000 --- a/src/app/screens/Nostr/ConfirmNip44Encrypt.tsx +++ /dev/null @@ -1,121 +0,0 @@ -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 2ab8ceb9ec..2fe61996f7 100644 --- a/src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/nip44DecryptOrPrompt.ts @@ -23,7 +23,10 @@ const nip44DecryptOrPrompt = async ( if (hasPermission) { const nostr = await state.getState().getNostr(); - const response = await nostr.nip44Decrypt(message.args.pairs); + const response = await nostr.nip44Decrypt( + message.args.peer, + message.args.ciphertext + ); return { data: response }; } else { @@ -32,7 +35,7 @@ const nip44DecryptOrPrompt = async ( rememberPermission: boolean; }>({ ...message, - action: "public/nostr/confirmNip44Decrypt", + action: "public/nostr/confirmDecrypt", }); // add permission to db only if user decided to always allow this request @@ -44,7 +47,10 @@ const nip44DecryptOrPrompt = async ( } if (promptResponse.data.confirm) { const nostr = await state.getState().getNostr(); - const response = await nostr.nip44Decrypt(message.args.pairs); + const response = await nostr.nip44Decrypt( + message.args.peer, + message.args.ciphertext + ); 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 2f59dd11a0..1c2e8eeac5 100644 --- a/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts @@ -1,4 +1,5 @@ import { USER_REJECTED_ERROR } from "~/common/constants"; +import nostr from "~/common/lib/nostr"; import utils from "~/common/lib/utils"; import { getHostFromSender } from "~/common/utils/helpers"; import { @@ -23,7 +24,8 @@ const nip44EncryptOrPrompt = async ( if (hasPermission) { const response = (await state.getState().getNostr()).nip44Encrypt( - message.args.pairs + message.args.peer, + message.args.plaintext ); return { data: response }; } else { @@ -32,10 +34,11 @@ const nip44EncryptOrPrompt = async ( rememberPermission: boolean; }>({ ...message, - action: "public/nostr/confirmNip44Encrypt", + action: "public/nostr/confirmEncrypt", args: { nip44Encrypt: { - pairs: message.args.pairs, + recipientNpub: nostr.hexToNip19(message.args.peer, "npub"), + message: message.args.plaintext, }, }, }); @@ -49,7 +52,8 @@ const nip44EncryptOrPrompt = async ( } if (promptResponse.data.confirm) { const response = (await state.getState().getNostr()).nip44Encrypt( - message.args.pairs + message.args.peer, + message.args.plaintext ); return { data: response }; diff --git a/src/extension/background-script/nostr/__test__/nostr.test.ts b/src/extension/background-script/nostr/__test__/nostr.test.ts index 88fe9ef18f..3db5956d9a 100644 --- a/src/extension/background-script/nostr/__test__/nostr.test.ts +++ b/src/extension/background-script/nostr/__test__/nostr.test.ts @@ -56,13 +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); }); @@ -71,15 +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 247ad27f41..0a4d0eeee1 100644 --- a/src/extension/background-script/nostr/index.ts +++ b/src/extension/background-script/nostr/index.ts @@ -82,16 +82,12 @@ class Nostr { return Utf8.stringify(decrypted); } - nip44Encrypt(pairs: [string, string][]) { - return pairs.map(([pubkey, text]: [string, string]) => - nip44.v2.encrypt(text, this.getNip44SharedSecret(pubkey)) - ); + nip44Encrypt(peer: string, plaintext: string) { + return nip44.v2.encrypt(plaintext, this.getNip44SharedSecret(peer)); } - nip44Decrypt(pairs: [string, string][]) { - return pairs.map(([pubkey, payload]) => - nip44.v2.decrypt(payload, this.getNip44SharedSecret(pubkey)) - ); + nip44Decrypt(peer: string, ciphertext: string) { + return nip44.v2.decrypt(ciphertext, this.getNip44SharedSecret(peer)) } getEventHash(event: Event) { diff --git a/src/extension/providers/nostr/index.ts b/src/extension/providers/nostr/index.ts index 54f0b7ffb5..48c264404e 100644 --- a/src/extension/providers/nostr/index.ts +++ b/src/extension/providers/nostr/index.ts @@ -79,13 +79,19 @@ class Nip44 { this.provider = provider; } - async encrypt(pairs: [string, string][]) { + async encrypt(peer: string, plaintext: string) { await this.provider.enable(); - return this.provider.execute("nip44EncryptOrPrompt", { pairs }); + return this.provider.execute("nip44EncryptOrPrompt", { + peer, + plaintext, + }); } - async decrypt(pairs: [string, string][]) { + async decrypt(peer: string, ciphertext: string) { await this.provider.enable(); - return this.provider.execute("nip44DecryptOrPrompt", { pairs }); + return this.provider.execute("nip44DecryptOrPrompt", { + peer, + ciphertext, + }); } } diff --git a/src/types.ts b/src/types.ts index 779603cfdd..19e1338450 100644 --- a/src/types.ts +++ b/src/types.ts @@ -176,7 +176,8 @@ export type NavigationState = { }; nip44Encrypt: { - pairs: [string, string][]; + recipientNpub: string; + message: string; }; psbt?: string; @@ -566,14 +567,16 @@ export interface MessageDecryptGet extends MessageDefault { export interface MessageNip44EncryptGet extends MessageDefault { args: { - pairs: [string, string][]; + peer: string; + plaintext: string; }; action: "encrypt"; } export interface MessageNip44DecryptGet extends MessageDefault { args: { - pairs: [string, string][]; + peer: string; + ciphertext: string; }; action: "decrypt"; } From 19182ba2217ad6757a591396e286bdd11fceb9f7 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Wed, 21 Feb 2024 10:02:35 +0700 Subject: [PATCH 5/8] fix: encryptOrPrompt args --- .../background-script/actions/nostr/nip44EncryptOrPrompt.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts b/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts index 1c2e8eeac5..5ab42232bc 100644 --- a/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts +++ b/src/extension/background-script/actions/nostr/nip44EncryptOrPrompt.ts @@ -36,7 +36,7 @@ const nip44EncryptOrPrompt = async ( ...message, action: "public/nostr/confirmEncrypt", args: { - nip44Encrypt: { + encrypt: { recipientNpub: nostr.hexToNip19(message.args.peer, "npub"), message: message.args.plaintext, }, From fde063471ab672856251451bf81f6f50dcaa6c77 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Wed, 21 Feb 2024 10:03:14 +0700 Subject: [PATCH 6/8] chore: run prettier --- src/extension/background-script/nostr/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extension/background-script/nostr/index.ts b/src/extension/background-script/nostr/index.ts index 0a4d0eeee1..589b2d09f5 100644 --- a/src/extension/background-script/nostr/index.ts +++ b/src/extension/background-script/nostr/index.ts @@ -87,7 +87,7 @@ class Nostr { } nip44Decrypt(peer: string, ciphertext: string) { - return nip44.v2.decrypt(ciphertext, this.getNip44SharedSecret(peer)) + return nip44.v2.decrypt(ciphertext, this.getNip44SharedSecret(peer)); } getEventHash(event: Event) { From 6959be9d5405072be765717bf739108805415748 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Wed, 21 Feb 2024 10:03:53 +0700 Subject: [PATCH 7/8] chore: update yarn.lock --- yarn.lock | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 39068a1ef4..9616eefcbc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1065,6 +1065,11 @@ resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-0.2.0.tgz#a12cda60f3cf1ab5d7c77068c3711d2366649ed7" integrity sha512-6YBxJDAapHSdd3bLDv6x2wRPwq4QFMUaB3HvljNBUTThDd12eSm7/3F+2lnfzx2jvM+S6Nsy0jEt9QbPqSwqRw== +"@noble/ciphers@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-0.5.1.tgz#292f388b69c9ed80d49dca1a5cbfd4ff06852111" + integrity sha512-aNE06lbe36ifvMbbWvmmF/8jx6EQPu2HVg70V95T+iGjOuYwPpAccwAQc2HlXO2D0aiQ3zavbMga4jjWnrpiPA== + "@noble/curves@1.1.0", "@noble/curves@~1.1.0": version "1.1.0" resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz" @@ -1072,6 +1077,13 @@ dependencies: "@noble/hashes" "1.3.1" +"@noble/curves@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + "@noble/curves@^1.3.0", "@noble/curves@~1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e" @@ -1084,16 +1096,16 @@ resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz" integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== +"@noble/hashes@1.3.2", "@noble/hashes@^1.1.5": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + "@noble/hashes@1.3.3", "@noble/hashes@~1.3.2": version "1.3.3" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== -"@noble/hashes@^1.1.5": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" - integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== - "@noble/hashes@^1.2.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" @@ -7243,6 +7255,25 @@ nostr-tools@^1.17.0: "@scure/bip32" "1.3.1" "@scure/bip39" "1.2.1" +nostr-tools@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.3.1.tgz#348d3c4aab0ab00716f93d6e2a72333d8c7da982" + integrity sha512-qjKx2C3EzwiQOe2LPSPyCnp07pGz1pWaWjDXcm+L2y2c8iTECbvlzujDANm3nJUjWL5+LVRUVDovTZ1a/DC4Bg== + dependencies: + "@noble/ciphers" "^0.5.1" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.1" + "@scure/base" "1.1.1" + "@scure/bip32" "1.3.1" + "@scure/bip39" "1.2.1" + optionalDependencies: + nostr-wasm v0.1.0 + +nostr-wasm@v0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/nostr-wasm/-/nostr-wasm-0.1.0.tgz#17af486745feb2b7dd29503fdd81613a24058d94" + integrity sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA== + npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" From f5893abe6faf8979957ab9875037578340683589 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Thu, 22 Feb 2024 09:02:34 -0800 Subject: [PATCH 8/8] Rename pk to peerPubkey --- src/extension/background-script/nostr/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/extension/background-script/nostr/index.ts b/src/extension/background-script/nostr/index.ts index 589b2d09f5..0324c7ff08 100644 --- a/src/extension/background-script/nostr/index.ts +++ b/src/extension/background-script/nostr/index.ts @@ -18,13 +18,13 @@ class Nostr { constructor(readonly privateKey: string) {} // Deriving shared secret is an expensive computation - getNip44SharedSecret(pk: string) { - let key = this.nip44SharedSecretCache.get(pk); + getNip44SharedSecret(peerPubkey: string) { + let key = this.nip44SharedSecretCache.get(peerPubkey); if (!key) { - key = nip44.v2.utils.getConversationKey(this.privateKey, pk); + key = nip44.v2.utils.getConversationKey(this.privateKey, peerPubkey); - this.nip44SharedSecretCache.set(pk, key); + this.nip44SharedSecretCache.set(peerPubkey, key); } return key;