diff --git a/README.md b/README.md
index 49792bc..1ed3212 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,8 @@ It also implements these optional methods:
async window.nostr.getRelays(): { [url: string]: {read: boolean, write: boolean} }
async window.nostr.nip04.encrypt(pubkey, plaintext): string
async window.nostr.nip04.decrypt(pubkey, ciphertext): string
+async window.nostr.nip44.encrypt(pubkey, plaintext): string
+async window.nostr.nip44.decrypt(pubkey, ciphertext): string
```
The repository is configured as monorepo to hold the extensions for Chrome and Firefox.
diff --git a/package-lock.json b/package-lock.json
index fa3d101..8c46684 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "gooti-extension",
- "version": "0.0.3",
+ "version": "0.0.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "gooti-extension",
- "version": "0.0.3",
+ "version": "0.0.4",
"dependencies": {
"@angular/animations": "^19.0.0",
"@angular/common": "^19.0.0",
diff --git a/package.json b/package.json
index dd5508d..e69ab48 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,12 @@
{
"name": "gooti-extension",
- "version": "0.0.3",
+ "version": "0.0.4",
"custom": {
"chrome": {
- "version": "0.0.3"
+ "version": "0.0.4"
},
"firefox": {
- "version": "0.0.3"
+ "version": "0.0.4"
}
},
"scripts": {
diff --git a/projects/chrome/public/manifest.json b/projects/chrome/public/manifest.json
index 2095aef..6bb6513 100644
--- a/projects/chrome/public/manifest.json
+++ b/projects/chrome/public/manifest.json
@@ -1,8 +1,8 @@
{
"manifest_version": 3,
- "name": "Gooti",
- "description": "Nostr Identity Manager & Signer",
- "version": "0.0.3",
+ "name": "Gooti - Nostr Identity Manager & Signer",
+ "description": "Manage and switch between multiple identities while interacting with Nostr apps",
+ "version": "0.0.4",
"homepage_url": "https://getgooti.com",
"options_page": "options.html",
"permissions": [
diff --git a/projects/chrome/public/prompt.html b/projects/chrome/public/prompt.html
index 22256a9..8d7b4d7 100644
--- a/projects/chrome/public/prompt.html
+++ b/projects/chrome/public/prompt.html
@@ -145,6 +145,29 @@
+
+
+
+
+ is requesting permission to
+
+ encrypt a text (NIP44)
+
+
+ for the selected identity
+
+
+
+
+
+
+
+
@@ -167,6 +190,29 @@
+
+
+
+
+
+ is requesting permission to
+
+ decrypt a text (NIP44)
+
+
+ for the selected identity
+
+
+
+
+
+
+
diff --git a/projects/chrome/src/background-common.ts b/projects/chrome/src/background-common.ts
index cfe6dde..b89db27 100644
--- a/projects/chrome/src/background-common.ts
+++ b/projects/chrome/src/background-common.ts
@@ -13,7 +13,7 @@ import {
Permission_ENCRYPTED,
} from '@common';
import { ChromeMetaHandler } from './app/common/data/chrome-meta-handler';
-import { Event, EventTemplate, finalizeEvent, nip04 } from 'nostr-tools';
+import { Event, EventTemplate, finalizeEvent, nip04, nip44 } from 'nostr-tools';
export const debug = function (message: any) {
const dateString = new Date().toISOString();
@@ -141,11 +141,21 @@ export const checkPermissions = function (
return permissions.every((x) => x.methodPolicy === 'allow');
}
+ if (method === 'nip44.encrypt') {
+ // No evaluation of params required.
+ return permissions.every((x) => x.methodPolicy === 'allow');
+ }
+
if (method === 'nip04.decrypt') {
// No evaluation of params required.
return permissions.every((x) => x.methodPolicy === 'allow');
}
+ if (method === 'nip44.decrypt') {
+ // No evaluation of params required.
+ return permissions.every((x) => x.methodPolicy === 'allow');
+ }
+
return undefined;
};
@@ -238,6 +248,18 @@ export const nip04Encrypt = async function (
);
};
+export const nip44Encrypt = async function (
+ privkey: string,
+ peerPubkey: string,
+ plaintext: string
+): Promise {
+ const key = nip44.v2.utils.getConversationKey(
+ NostrHelper.hex2bytes(privkey),
+ peerPubkey
+ );
+ return nip44.v2.encrypt(plaintext, key);
+};
+
export const nip04Decrypt = async function (
privkey: string,
peerPubkey: string,
@@ -250,6 +272,19 @@ export const nip04Decrypt = async function (
);
};
+export const nip44Decrypt = async function (
+ privkey: string,
+ peerPubkey: string,
+ ciphertext: string
+): Promise {
+ const key = nip44.v2.utils.getConversationKey(
+ NostrHelper.hex2bytes(privkey),
+ peerPubkey
+ );
+
+ return nip44.v2.decrypt(ciphertext, key);
+};
+
const encryptPermission = async function (
permission: Permission_DECRYPTED,
iv: string,
diff --git a/projects/chrome/src/background.ts b/projects/chrome/src/background.ts
index 08113aa..6c33232 100644
--- a/projects/chrome/src/background.ts
+++ b/projects/chrome/src/background.ts
@@ -8,6 +8,8 @@ import {
getPosition,
nip04Decrypt,
nip04Encrypt,
+ nip44Decrypt,
+ nip44Encrypt,
PromptResponse,
PromptResponseMessage,
signEvent,
@@ -135,6 +137,13 @@ browser.runtime.onMessage.addListener(async (message /*, sender*/) => {
req.params.plaintext
);
+ case 'nip44.encrypt':
+ return await nip44Encrypt(
+ currentIdentity.privkey,
+ req.params.peerPubkey,
+ req.params.plaintext
+ );
+
case 'nip04.decrypt':
return await nip04Decrypt(
currentIdentity.privkey,
@@ -142,6 +151,13 @@ browser.runtime.onMessage.addListener(async (message /*, sender*/) => {
req.params.ciphertext
);
+ case 'nip44.decrypt':
+ return await nip44Decrypt(
+ currentIdentity.privkey,
+ req.params.peerPubkey,
+ req.params.ciphertext
+ );
+
default:
throw new Error(`Not supported request method '${req.method}'.`);
}
diff --git a/projects/chrome/src/gooti-extension.ts b/projects/chrome/src/gooti-extension.ts
index 0c19f55..35f90a5 100644
--- a/projects/chrome/src/gooti-extension.ts
+++ b/projects/chrome/src/gooti-extension.ts
@@ -110,15 +110,29 @@ const nostr = {
},
},
- // nip44: {
- // async encrypt(peer, plaintext) {
- // return window.nostr._call('nip44.encrypt', { peer, plaintext });
- // },
+ nip44: {
+ async encrypt(peerPubkey: string, plaintext: string): Promise {
+ debug('nip44.encrypt received');
+ const ciphertext = (await nostr.messenger.request('nip44.encrypt', {
+ peerPubkey,
+ plaintext,
+ })) as string;
+ debug('nip44.encrypt response:');
+ debug(ciphertext);
+ return ciphertext;
+ },
- // async decrypt(peer, ciphertext) {
- // return window.nostr._call('nip44.decrypt', { peer, ciphertext });
- // },
- // },
+ async decrypt(peerPubkey: string, ciphertext: string): Promise {
+ debug('nip44.decrypt received');
+ const plaintext = (await nostr.messenger.request('nip44.decrypt', {
+ peerPubkey,
+ ciphertext,
+ })) as string;
+ debug('nip44.decrypt response:');
+ debug(plaintext);
+ return plaintext;
+ },
+ },
};
window.nostr = nostr as any;
diff --git a/projects/chrome/src/prompt.ts b/projects/chrome/src/prompt.ts
index 0032df3..95aa6fc 100644
--- a/projects/chrome/src/prompt.ts
+++ b/projects/chrome/src/prompt.ts
@@ -24,10 +24,18 @@ switch (method) {
title = 'Encrypt';
break;
+ case 'nip44.encrypt':
+ title = 'Encrypt';
+ break;
+
case 'nip04.decrypt':
title = 'Decrypt';
break;
+ case 'nip44.decrypt':
+ title = 'Decrypt';
+ break;
+
case 'getRelays':
title = 'Get Relays';
break;
@@ -110,6 +118,23 @@ if (cardNip04EncryptElement && card2Nip04EncryptElement) {
}
}
+const cardNip44EncryptElement = document.getElementById('cardNip44Encrypt');
+const card2Nip44EncryptElement = document.getElementById('card2Nip44Encrypt');
+if (cardNip44EncryptElement && card2Nip44EncryptElement) {
+ if (method === 'nip44.encrypt') {
+ const card2Nip44Encrypt_textElement = document.getElementById(
+ 'card2Nip44Encrypt_text'
+ );
+ if (card2Nip44Encrypt_textElement) {
+ const eventObject: { peerPubkey: string; plaintext: string } =
+ JSON.parse(event);
+ card2Nip44Encrypt_textElement.innerText = eventObject.plaintext;
+ }
+ } else {
+ cardNip44EncryptElement.style.display = 'none';
+ card2Nip44EncryptElement.style.display = 'none';
+ }
+}
const cardNip04DecryptElement = document.getElementById('cardNip04Decrypt');
const card2Nip04DecryptElement = document.getElementById('card2Nip04Decrypt');
if (cardNip04DecryptElement && card2Nip04DecryptElement) {
@@ -128,6 +153,24 @@ if (cardNip04DecryptElement && card2Nip04DecryptElement) {
}
}
+const cardNip44DecryptElement = document.getElementById('cardNip44Decrypt');
+const card2Nip44DecryptElement = document.getElementById('card2Nip44Decrypt');
+if (cardNip44DecryptElement && card2Nip44DecryptElement) {
+ if (method === 'nip44.decrypt') {
+ const card2Nip44Decrypt_textElement = document.getElementById(
+ 'card2Nip44Decrypt_text'
+ );
+ if (card2Nip44Decrypt_textElement) {
+ const eventObject: { peerPubkey: string; ciphertext: string } =
+ JSON.parse(event);
+ card2Nip44Decrypt_textElement.innerText = eventObject.ciphertext;
+ }
+ } else {
+ cardNip44DecryptElement.style.display = 'none';
+ card2Nip44DecryptElement.style.display = 'none';
+ }
+}
+
//
// Functions
//
diff --git a/projects/common/src/lib/models/nostr.ts b/projects/common/src/lib/models/nostr.ts
index 3168ca3..5fc4686 100644
--- a/projects/common/src/lib/models/nostr.ts
+++ b/projects/common/src/lib/models/nostr.ts
@@ -3,6 +3,8 @@ export type Nip07Method =
| 'getPublicKey'
| 'getRelays'
| 'nip04.encrypt'
- | 'nip04.decrypt';
+ | 'nip04.decrypt'
+ | 'nip44.encrypt'
+ | 'nip44.decrypt';
export type Nip07MethodPolicy = 'allow' | 'deny';
diff --git a/projects/firefox/public/manifest.json b/projects/firefox/public/manifest.json
index a9bc34a..1b4faa1 100644
--- a/projects/firefox/public/manifest.json
+++ b/projects/firefox/public/manifest.json
@@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "Gooti",
"description": "Nostr Identity Manager & Signer",
- "version": "0.0.3",
+ "version": "0.0.4",
"homepage_url": "https://getgooti.com",
"options_page": "options.html",
"permissions": [
diff --git a/projects/firefox/public/prompt.html b/projects/firefox/public/prompt.html
index 22256a9..8d7b4d7 100644
--- a/projects/firefox/public/prompt.html
+++ b/projects/firefox/public/prompt.html
@@ -145,6 +145,29 @@
+
+
+
+
+ is requesting permission to
+
+ encrypt a text (NIP44)
+
+
+ for the selected identity
+
+
+
+
+
+
+
+
@@ -167,6 +190,29 @@
+
+
+
+
+
+ is requesting permission to
+
+ decrypt a text (NIP44)
+
+
+ for the selected identity
+
+
+
+
+
+
+
diff --git a/projects/firefox/src/background-common.ts b/projects/firefox/src/background-common.ts
index d942174..bad95e6 100644
--- a/projects/firefox/src/background-common.ts
+++ b/projects/firefox/src/background-common.ts
@@ -12,7 +12,7 @@ import {
Permission_DECRYPTED,
Permission_ENCRYPTED,
} from '@common';
-import { Event, EventTemplate, finalizeEvent, nip04 } from 'nostr-tools';
+import { Event, EventTemplate, finalizeEvent, nip04, nip44 } from 'nostr-tools';
import { FirefoxMetaHandler } from './app/common/data/firefox-meta-handler';
import browser from 'webextension-polyfill';
@@ -146,11 +146,21 @@ export const checkPermissions = function (
return permissions.every((x) => x.methodPolicy === 'allow');
}
+ if (method === 'nip44.encrypt') {
+ // No evaluation of params required.
+ return permissions.every((x) => x.methodPolicy === 'allow');
+ }
+
if (method === 'nip04.decrypt') {
// No evaluation of params required.
return permissions.every((x) => x.methodPolicy === 'allow');
}
+ if (method === 'nip44.decrypt') {
+ // No evaluation of params required.
+ return permissions.every((x) => x.methodPolicy === 'allow');
+ }
+
return undefined;
};
@@ -243,6 +253,18 @@ export const nip04Encrypt = async function (
);
};
+export const nip44Encrypt = async function (
+ privkey: string,
+ peerPubkey: string,
+ plaintext: string
+): Promise {
+ const key = nip44.v2.utils.getConversationKey(
+ NostrHelper.hex2bytes(privkey),
+ peerPubkey
+ );
+ return nip44.v2.encrypt(plaintext, key);
+};
+
export const nip04Decrypt = async function (
privkey: string,
peerPubkey: string,
@@ -255,6 +277,19 @@ export const nip04Decrypt = async function (
);
};
+export const nip44Decrypt = async function (
+ privkey: string,
+ peerPubkey: string,
+ ciphertext: string
+): Promise {
+ const key = nip44.v2.utils.getConversationKey(
+ NostrHelper.hex2bytes(privkey),
+ peerPubkey
+ );
+
+ return nip44.v2.decrypt(ciphertext, key);
+};
+
const encryptPermission = async function (
permission: Permission_DECRYPTED,
iv: string,
diff --git a/projects/firefox/src/background.ts b/projects/firefox/src/background.ts
index 08113aa..6c33232 100644
--- a/projects/firefox/src/background.ts
+++ b/projects/firefox/src/background.ts
@@ -8,6 +8,8 @@ import {
getPosition,
nip04Decrypt,
nip04Encrypt,
+ nip44Decrypt,
+ nip44Encrypt,
PromptResponse,
PromptResponseMessage,
signEvent,
@@ -135,6 +137,13 @@ browser.runtime.onMessage.addListener(async (message /*, sender*/) => {
req.params.plaintext
);
+ case 'nip44.encrypt':
+ return await nip44Encrypt(
+ currentIdentity.privkey,
+ req.params.peerPubkey,
+ req.params.plaintext
+ );
+
case 'nip04.decrypt':
return await nip04Decrypt(
currentIdentity.privkey,
@@ -142,6 +151,13 @@ browser.runtime.onMessage.addListener(async (message /*, sender*/) => {
req.params.ciphertext
);
+ case 'nip44.decrypt':
+ return await nip44Decrypt(
+ currentIdentity.privkey,
+ req.params.peerPubkey,
+ req.params.ciphertext
+ );
+
default:
throw new Error(`Not supported request method '${req.method}'.`);
}
diff --git a/projects/firefox/src/gooti-extension.ts b/projects/firefox/src/gooti-extension.ts
index 0c19f55..35f90a5 100644
--- a/projects/firefox/src/gooti-extension.ts
+++ b/projects/firefox/src/gooti-extension.ts
@@ -110,15 +110,29 @@ const nostr = {
},
},
- // nip44: {
- // async encrypt(peer, plaintext) {
- // return window.nostr._call('nip44.encrypt', { peer, plaintext });
- // },
+ nip44: {
+ async encrypt(peerPubkey: string, plaintext: string): Promise {
+ debug('nip44.encrypt received');
+ const ciphertext = (await nostr.messenger.request('nip44.encrypt', {
+ peerPubkey,
+ plaintext,
+ })) as string;
+ debug('nip44.encrypt response:');
+ debug(ciphertext);
+ return ciphertext;
+ },
- // async decrypt(peer, ciphertext) {
- // return window.nostr._call('nip44.decrypt', { peer, ciphertext });
- // },
- // },
+ async decrypt(peerPubkey: string, ciphertext: string): Promise {
+ debug('nip44.decrypt received');
+ const plaintext = (await nostr.messenger.request('nip44.decrypt', {
+ peerPubkey,
+ ciphertext,
+ })) as string;
+ debug('nip44.decrypt response:');
+ debug(plaintext);
+ return plaintext;
+ },
+ },
};
window.nostr = nostr as any;
diff --git a/projects/firefox/src/prompt.ts b/projects/firefox/src/prompt.ts
index 0032df3..279c880 100644
--- a/projects/firefox/src/prompt.ts
+++ b/projects/firefox/src/prompt.ts
@@ -24,10 +24,18 @@ switch (method) {
title = 'Encrypt';
break;
+ case 'nip44.encrypt':
+ title = 'Encrypt';
+ break;
+
case 'nip04.decrypt':
title = 'Decrypt';
break;
+ case 'nip44.decrypt':
+ title = 'Decrypt';
+ break;
+
case 'getRelays':
title = 'Get Relays';
break;
@@ -110,6 +118,24 @@ if (cardNip04EncryptElement && card2Nip04EncryptElement) {
}
}
+const cardNip44EncryptElement = document.getElementById('cardNip44Encrypt');
+const card2Nip44EncryptElement = document.getElementById('card2Nip44Encrypt');
+if (cardNip44EncryptElement && card2Nip44EncryptElement) {
+ if (method === 'nip44.encrypt') {
+ const card2Nip44Encrypt_textElement = document.getElementById(
+ 'card2Nip44Encrypt_text'
+ );
+ if (card2Nip44Encrypt_textElement) {
+ const eventObject: { peerPubkey: string; plaintext: string } =
+ JSON.parse(event);
+ card2Nip44Encrypt_textElement.innerText = eventObject.plaintext;
+ }
+ } else {
+ cardNip44EncryptElement.style.display = 'none';
+ card2Nip44EncryptElement.style.display = 'none';
+ }
+}
+
const cardNip04DecryptElement = document.getElementById('cardNip04Decrypt');
const card2Nip04DecryptElement = document.getElementById('card2Nip04Decrypt');
if (cardNip04DecryptElement && card2Nip04DecryptElement) {
@@ -128,6 +154,24 @@ if (cardNip04DecryptElement && card2Nip04DecryptElement) {
}
}
+const cardNip44DecryptElement = document.getElementById('cardNip44Decrypt');
+const card2Nip44DecryptElement = document.getElementById('card2Nip44Decrypt');
+if (cardNip44DecryptElement && card2Nip44DecryptElement) {
+ if (method === 'nip44.decrypt') {
+ const card2Nip44Decrypt_textElement = document.getElementById(
+ 'card2Nip44Decrypt_text'
+ );
+ if (card2Nip44Decrypt_textElement) {
+ const eventObject: { peerPubkey: string; ciphertext: string } =
+ JSON.parse(event);
+ card2Nip44Decrypt_textElement.innerText = eventObject.ciphertext;
+ }
+ } else {
+ cardNip44DecryptElement.style.display = 'none';
+ card2Nip44DecryptElement.style.display = 'none';
+ }
+}
+
//
// Functions
//