Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/susumuota/nostr-keyx

A NIP-07 browser extension that uses the OS's keychain or YubiKey to protect your private keys.
https://github.com/susumuota/nostr-keyx

browser-extension chrome-extension encryption keychain nip-07 nodejs nostr typescript yubico yubikey

Last synced: 27 days ago
JSON representation

A NIP-07 browser extension that uses the OS's keychain or YubiKey to protect your private keys.

Awesome Lists containing this project

README

        

# nostr-keyx: Nostr Key Management Extension

[![GitHub release](https://img.shields.io/github/v/release/susumuota/nostr-keyx)](https://github.com/susumuota/nostr-keyx/releases)
[![GitHub License](https://img.shields.io/github/license/susumuota/nostr-keyx)](https://github.com/susumuota/nostr-keyx/blob/main/LICENSE)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/susumuota/nostr-keyx/build.yaml)](https://github.com/susumuota/nostr-keyx/actions/workflows/build.yaml)
[![GitHub last commit](https://img.shields.io/github/last-commit/susumuota/nostr-keyx)](https://github.com/susumuota/nostr-keyx/commits)

EN |
[JA](https://github-com.translate.goog/susumuota/nostr-keyx/blob/main/README.md?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja&_x_tr_pto=wapp) |
[ES](https://github-com.translate.goog/susumuota/nostr-keyx/blob/main/README.md?_x_tr_sl=en&_x_tr_tl=es&_x_tr_hl=es&_x_tr_pto=wapp) |
[ZH](https://github-com.translate.goog/susumuota/nostr-keyx/blob/main/README.md?_x_tr_sl=en&_x_tr_tl=zh-CN&_x_tr_hl=zh-CN&_x_tr_pto=wapp)

A [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md) browser extension that uses the OS's keychain or YubiKey to protect your private keys.

- **OS's native keychain application** support ([macOS](https://support.apple.com/guide/keychain-access/what-is-keychain-access-kyca1083/mac), [Windows](https://support.microsoft.com/en-us/windows/accessing-credential-manager-1b5c916a-6a16-889f-8581-fc16e8165ac0), [Linux](https://www.passwordstore.org/))
- **YubiKey OpenPGP** support ([YubiKey 5 Series](https://www.yubico.com/products/yubikey-5-overview/))
- Minimal dependencies ([`@noble/secp256k1`](https://github.com/paulmillr/noble-secp256k1) and [`@scure/base`](https://github.com/paulmillr/scure-base))

![keychain](https://user-images.githubusercontent.com/1632335/221407817-bc4ecd58-da53-4b39-8ec0-59270eee6af9.png)

There are already great extensions like [nos2x](https://github.com/fiatjaf/nos2x) or [Alby](https://getalby.com/) for [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md). Unlike these existing extensions, `nostr-keyx` uses the **OS's native keychain application** or **YubiKey** to store your private key instead of the web browser's local storage. Your private keys are encrypted by the operating system or by YubiKey. In addition, all of the [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md) functions (`signEvent`, `encrypt`, `decrypt`, etc.) are executed outside of the web browser's memory. So it might be less risky than other extensions. I hope this extension helps you too.

## Download

- There are 2 options to download `nostr-keyx`.

### Option 1: Download zip file

- Download the latest zip file from [Releases](https://github.com/susumuota/nostr-keyx/releases).
- Unzip it. `nostr-keyx-{version}.zip` will be extracted to `nostr-keyx-{version}` folder.

### Option 2: Download with `git` and build from source

> **Note**: For Windows, install [Git for Windows](https://gitforwindows.org/), start `git-bash` and run `npm config set script-shell /usr/bin/bash`. Otherwise, you will get error at `npm run build`.

```sh
# install latest stable version of Node.js
node -v # I have tested on v18.16.0
git clone https://github.com/susumuota/nostr-keyx.git
cd nostr-keyx
npm ci
npm run build
```

## Install

- You need to install a Chrome extension, Node.js and a Chrome native messaging host to run `nostr-keyx`.

### Install Chrome extension

- Open Chrome's extensions setting page `chrome://extensions`.
- Turn `Developer mode` on.
- Click `Load unpacked`.
- Specify the extension folder `/path/to/dist/extension`.
- You will see error messages but it's OK for now.
- Copy the `id` of the extension. e.g. `jhpjgkhjimkbjiigognoefgnclgngklh`. We will use it later.

### Install Node.js

- `nostr-keyx` uses [Node.js](https://nodejs.org/) to provide NIP-07 functions and access the OS's native keychain application.
- Install [Node.js](https://nodejs.org/) and make sure `node` command is available in your terminal (type `which node` to confirm).

### Install Chrome native messaging host

- This Chrome extension uses [Chrome Native Messaging](https://developer.chrome.com/docs/apps/nativeMessaging/) to communicate with a native Node.js script.
- You need to install a native messaging host file which is a JSON file that specifies the absolute path of the Node.js script.

#### For macOS and Linux

> **Note**: I recommend that you should check the content of `install.sh` before you run it. I have tested it in my environment, but I cannot guarantee anything. Basically, `install.sh` performs the steps on [this page](https://developer.chrome.com/docs/apps/nativeMessaging/#native-messaging-host) in `bash` script.

- Run [`install.sh`](https://github.com/susumuota/nostr-keyx/blob/main/public/macos/install.sh) to install the native messaging host.

```sh
cd /path/to/dist/macos # or linux
cat ./install.sh # confirm before you run it
bash ./install.sh # or bash ./install.sh
```

- Paste the `id` of the extension. e.g. `jhpjgkhjimkbjiigognoefgnclgngklh`. You can find the `id` of the extension in Chrome's extensions setting page `chrome://extensions`.
- If you want to uninstall the native messaging host, run [`uninstall.sh`](https://github.com/susumuota/nostr-keyx/blob/main/public/macos/uninstall.sh).

```sh
cat ./uninstall.sh # confirm before you run it
bash ./uninstall.sh
```

#### For Windows

- First, you need to allow PowerShell to run scripts.
- Open PowerShell as an **Administrator**.
- Run the following command to allow executing script. See details [here](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.3#remotesigned).

```powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
```

- Exit PowerShell of **Administrator**.
- Open PowerShell as a **normal user**.
- Run `Unblock-File` to unblock PowerShell script files that were downloaded from the internet so you can run them. See details [here](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/unblock-file?view=powershell-7.3).

```powershell
cd C:\path\to\dist\windows
Unblock-File .\install.ps1
Unblock-File .\uninstall.ps1
Unblock-File .\add_privatekey.ps1
Unblock-File .\get_privatekey.ps1
```

> **Note**: I recommend that you should check the contents of PowerShell script files before you run them. I have tested them in my environment, but I cannot guarantee anything. Basically, `install.ps1` performs the steps on [this page](https://developer.chrome.com/docs/apps/nativeMessaging/#native-messaging-host) in PowerShell.

- Run [`install.ps1`](https://github.com/susumuota/nostr-keyx/blob/main/public/windows/install.ps1) to install the native messaging host.

```powershell
cat .\install.ps1 # confirm before you run it
.\install.ps1
```

- Paste the `id` of the extension. e.g. `jhpjgkhjimkbjiigognoefgnclgngklh`. You can find the `id` of the extension in Chrome's extensions setting page `chrome://extensions`.
- If you want to uninstall the native messaging host, run [`uninstall.ps1`](https://github.com/susumuota/nostr-keyx/blob/main/public/windows/uninstall.ps1).

```powershell
cat .\uninstall.ps1 # confirm before you run it
.\uninstall.ps1
```

## Setup

- Save your private key to the OS's native keychain application.
- You need to specify the private key with account name and service name to switch between multiple private keys.
- The default account name must be `default`. You can add other account names, but they must consist of alphanumeric characters, underscores, periods or hyphens.
- At the moment, the service name must be `nostr-keyx`.

> **Note**: If you need private keys for test, you can generate them with `npm run genkey` (needs source, see option 2 above).

### For macOS: Option 1: Using command `security`

- Here, I show you how to save your private key on Terminal. You can also use [Keychain Access](https://support.apple.com/guide/keychain-access/what-is-keychain-access-kyca1083/mac). I will show you later.
- Copy private key (e.g. `nsec1...`) to clipboard.
- Open Terminal.
- Run `security add-generic-password` command to create a new entry for your private key. Here, `-a` specifies the account name e.g `default`, `-s` specifies the service name (service **MUST** be `nostr-keyx`), and `-w` means the password will be asked.

```sh
security add-generic-password -a default -s nostr-keyx -w
# paste your private key (e.g. nsec1....)
# paste it again
```

- Confirm that the entry is created. `find-generic-password` sub command will show the password.

```sh
security find-generic-password -a default -s nostr-keyx -w
```

- If you want to delete the entry, run `delete-generic-password` sub command.

```sh
security delete-generic-password -a default -s nostr-keyx
```

- You can create multiple accounts for multiple private keys. e.g. `default`, `bot`, `test`, etc. But service name **MUST** be `nostr-keyx`.

> **Note**: Right now, `security` command can access the private key without password. But you can revoke that by Keychain Access application. See the next section.

### For macOS: Option 2: Using [Keychain Access](https://support.apple.com/guide/keychain-access/what-is-keychain-access-kyca1083/mac)

- Open spotlight search and type `Keychain Access` and open it.
- `File` menu > `New Password Item...`
- Type `nostr-keyx` to `Keychain Item Name` (the first text field).
- Type `default` to `Account Name` (the second text field).
- Copy private key (e.g. `nsec1...`) to clipboard.
- Paste it to `Password` (the third text field).
- Click `Add`.

![new_password_item](https://user-images.githubusercontent.com/1632335/220175161-a5c0daa7-62bc-45bb-a270-ffa8a78cd80f.png)

- You can create multiple accounts for multiple private keys. But make sure that the service name is `nostr-keyx`.
- Open Terminal and run `security` command to confirm that the private key can be accessed via `security` command.

```sh
security find-generic-password -a default -s nostr-keyx -w
```

> **Note**: When you try to access private key, you will be asked to enter your password. You can click `Always Allow` to allow the access without password. When you want to revoke that, you can change the access control of the entry. Right click the entry and select `Get Info`. Then, click `Access Control` tab and click `security` on `Always allow access by these applications:` area then click `-` button to remove it. Now you will be asked to enter your password when you try to access the private key via `security` command.

![revoke_application](https://user-images.githubusercontent.com/1632335/220175649-39b206cc-a845-4c48-83ec-367668aacabe.png)

### For Windows: Using command `add_privatekey.ps1`

- Confirm content of [`add_privatekey.ps1`](https://github.com/susumuota/nostr-keyx/blob/main/public/windows/add_privatekey.ps1) whether it is safe to run. See details [here](https://learn.microsoft.com/en-us/uwp/api/windows.security.credentials.passwordvault?view=winrt-22621) and [here](https://learn.microsoft.com/en-us/uwp/api/windows.security.credentials.passwordcredential?view=winrt-22621).
- Copy private key (e.g. `nsec1...`) to clipboard.
- Run `add_privatekey.ps1` to create a new entry for your private key. You **MUST** pass `nostr-keyx` as an argument.

```powershell
cat .\add_privatekey.ps1 # confirm before you run it
.\add_privatekey.ps1 "nostr-keyx"
```

- Dialog will be shown. Type `default` to `User name` field, paste your private key to `Password` field, then click `OK`.

![get_credential](https://user-images.githubusercontent.com/1632335/221339350-122fa0c2-e0a4-4843-bdd4-8fef58aec3a8.png)

- Confirm content of [`get_privatekey.ps1`](https://github.com/susumuota/nostr-keyx/blob/main/public/windows/get_privatekey.ps1) whether it is safe to run. See details [here](https://learn.microsoft.com/en-us/uwp/api/windows.security.credentials.passwordvault?view=winrt-22621) and [here](https://learn.microsoft.com/en-us/uwp/api/windows.security.credentials.passwordcredential?view=winrt-22621).
- Run `get_privatekey.ps1` to get your private key.

```powershell
cat .\get_privatekey.ps1 # confirm before you run it
.\get_privatekey.ps1 "default" "nostr-keyx"
```

- Type `credential manager` in the search box on the taskbar and select [Credential Manager](https://support.microsoft.com/en-us/windows/accessing-credential-manager-1b5c916a-6a16-889f-8581-fc16e8165ac0) Control panel.
- Click `Web Credentials` and you will see the entry for your private key.

![credential_manager](https://user-images.githubusercontent.com/1632335/221339296-9fa1eddb-bcff-47c1-859f-0ac717f2bf81.png)

### For Linux: Using command `pass`

- Setup `pass`. See [this page](https://www.passwordstore.org/).
- Copy private key (e.g. `nsec1...`) to clipboard.
- Run `pass insert` command to create a new entry for your private key.

```sh
pass insert nostr-keyx/default
# paste your private key (e.g. nsec1....)
# paste it again
```

### For YubiKey: Using command `gpg`

- Setup OpenPGP with YubiKey. Follow [my setup memo](https://github.com/susumuota/yubikey-setup#generate-gpg-keys) or [this article](https://support.yubico.com/hc/en-us/articles/360013790259-Using-Your-YubiKey-with-OpenPGP).
- Install `pinentry-mac` (for macOS) or GUI-based `pinentry` (for Linux).

```sh
brew install pinentry-mac
```

- Encrypt (and sign) the Nostr private key with gpg and YubiKey.

```sh
cd /path/to/dist/macos # or linux
gpg -sea --default-recipient-self > nostr_privatekey.asc
# paste the private key, enter, and Ctrl+D
```

- Try to decrypt it.

```sh
gpg -d nostr_privatekey.asc
```

- Unplug YubiKey and try again, it should fail

```sh
gpg -d nostr_privatekey.asc
```

- Plug YubiKey again.
- Run `yubikey.sh` script.

```sh
/bin/sh -c ./yubikey.sh 2> /dev/null
```

- It should decrypt the private key successfully.
- Don't forget to add an account `yubikey` on extension popup settings. See `Usage` section below.

### Test it on Iris or Snort

- Open Chrome and go to `chrome://extensions/`.
- Disable similar NIP-07 extensions, e.g. nos2x, Alby, etc. On Alby, you only need to disable `NIP-07` section on the settings.
- Clear errors of the extension.
- Reload the extension.
- Click `Service Worker` to open dev console of the extension.
- If you have used previous versions of this extension, you should clear the extension's cache. Type the following commands in the extension's dev console.

```javascript
await chrome.storage.local.clear();
await chrome.storage.sync.clear();
await chrome.storage.session.clear();
```

- Enable log level `Verbose` to show debug logs.
- Go to [Iris](https://iris.to/) or [Snort](https://snort.social/).
- Logout if you already logged in.
- Click `Nostr extension login` for Iris or `Login with Extension (NIP-07)` for Snort. It should use `window.nostr.getPublicKey` to get public key.
- Post some notes. It should use `window.nostr.signEvent` to sign events with private key.
- Send/receive direct messages. It should use `window.nostr.nip04.encrypt/decrypt` to encrypt/decrypt messages.

## Usage

- You can change account (private key) by popup UI of the extension.

![popup](https://user-images.githubusercontent.com/1632335/224256295-9629952f-4596-4c65-9166-09b3c73f7279.png)

- You can add an account. This account name must match the account name in the OS Keychain application. e.g. `default`, `yubikey`, `bot`, etc.
- To use YubiKey, you need to add an account `yubikey`.
- Press `+` button and enter your account name, then press `ADD`.
- You can switch accounts by selecting list items on the popup UI.
- You can also add a URL to allow the web page to use the extension.
- Press `+` button and enter a new URL, then press `ADD`.

## Experimental features

- The popup icon shows the number of API calls accessing the private key (`signEvent`, `encrypt` and `decrypt`). You should keep your eyes on the **growth of this number** in order to monitor the behavior of web-based Nostr clients. The number is reset to zero when it reaches 10 due to space limitations in the popup icon.

![image](https://user-images.githubusercontent.com/1632335/222151354-57118ef0-f76b-4e7e-acf3-3c1d915acdd6.png)

## Potential risks

- This extension passes your private key to [secp256k1.schnorr.sign](https://github.com/paulmillr/noble-secp256k1#schnorrsignmessage-privatekey) and [secp256k1.getSharedSecret](https://github.com/paulmillr/noble-secp256k1#getsharedsecretprivatekeya-publickeyb). The security of this extension depends heavily on these functions.
- Let me know if you find any other potential risks.

## TODO

- [x] Prepare a zip file for easy installation.
- [x] Find a way to store the private key securely.
- [x] Find a way to store the AES key securely.
- [x] Find a way to access OS's Keychain app or Chrome's password manager from the Chrome extension.
- [x] Minimal UI.
- [x] Add profiles to switch multiple accounts.
- [x] Add Windows keychain applications support.
- [x] GitHub Actions to build and publish the zip file.
- [x] Add installer script.
- [x] UI to guide users to install the native messaging host.
- [x] Add preliminary YubiKey support.
- [ ] Add biometric authentication support. e.g. fingerprint sensor for MacBook Pro.
- [ ] Add a stats page to show the number of API calls accessing the private key.
- [ ] Support [NIP-46](https://github.com/nostr-protocol/nips/blob/master/46.md).
- [ ] Better error handling.
- [ ] Test `relays`.
- [ ] Chrome Web Store?
- [ ] Add YubiKey native support. At the moment, it looks hard. Related information: [here](https://support.yubico.com/hc/en-us/articles/360016649139-YubiKey-5-2-Enhancements-to-OpenPGP-3-4-Support), [here](https://bitcoindev.network/using-gpg-as-a-bitcoin-address/) and [here](https://github.com/Yubico/yubico-piv-tool/issues/405#issuecomment-1289079879).

## Source code

- https://github.com/susumuota/nostr-keyx

## Related Links

- [NIP-07](https://github.com/nostr-protocol/nips/blob/master/07.md): `window.nostr` capability for web browsers.
- [nos2x](https://github.com/fiatjaf/nos2x): Chrome Extension for NIP-07.
- [nostr-tools](https://github.com/nbd-wtf/nostr-tools): Tools for developing Nostr clients.
- [noble-secp256k1](https://github.com/paulmillr/noble-secp256k1): JavaScript implementation of [secp256k1](https://www.secg.org/sec2-v2.pdf).
- [scure-base](https://github.com/paulmillr/scure-base): Secure implementation of base64, etc.
- [Iris](https://iris.to/): Web-based Nostr client.
- [Snort](https://snort.social/): Web-based Nostr client.

## License

MIT License, see [LICENSE](LICENSE) file.

## Author

S. Ota

- nostr: [`npub1susumuq8u7v0sp2f5jl3wjuh8hpc3cqe2tc2j5h4gu7ze7z20asq2w0yu8`](https://iris.to/s_ota)
- GitHub: [susumuota](https://github.com/susumuota)
- Twitter: [@susumuota](https://twitter.com/susumuota)