How npubcash-desktop protects your private key

This week, I began developing a native desktop client for npub.cash. My primary goal is to create a tool that can run in the background and notify users whenever they receive a payment. Additionally, I saw this project as an opportunity to learn how to use Electron. A major concern in building this application is ensuring the security of users' private keys. Leaking a private key is disastrous, as the nsec serves as the master key to a user’s nostr identity, and recovery from a leak is difficult. Every nostr developer must prioritize the protection of a user's nsec.

Using NIP-49 ncrypt

To provide a smooth user experience, clients need to allow users to input their private keys and then securely store them for future use. Otherwise, users would have to enter their keys every time they start the app, which is impractical. However, this approach carries risks. An attacker could access the part of your file system where the key is stored or spy on your clipboard when you are pasting the key.

Fortunately, there's a solution that has been part of the protocol for a few months: NIP-49 ncrypt. An ncrypt is an encrypted version of your private key, created using a standardized encryption process and a passphrase you choose. Because the encryption is standardized, it can be used across all clients that support this NIP. This means that even if an attacker gains access to your filesystem or clipboard, they would only see the encrypted key. As long as your passphrase is strong, it will be extremely difficult for them to obtain your private key, even if the ncrypt is compromised.

npubcash-desktop setup page

npubcash-desktop supports ncrypt and uses it everywhere. As a result, it will only ever store the encrypted key on your filesystem.

But What About Memory?

While ncrypt secures your private key on the filesystem, the app itself needs the raw key to sign notes and perform other actions before transmitting them to relays. If the app decrypts the key once you enter your passphrase and keeps it in memory, a skilled attacker could attempt to read it from memory.

npubcash-desktop uses Electron's safeStorage API to encrypt the raw private key again and will only keep the encrypted string in memory. SafeStorage uses the OS's native keychain (often stored on very secure parts of the machine) to encrypt data. Whenever the client needs your key to perform any action, it will read the encrypted one from memory, pass it to safeStorage, which will return the raw key and wipe it from memory after the action has been performed. In a high-level language like JavaScript, you usually have to rely on the garbage collector to free memory for you. However, both safeStorage and the npubcash-desktop signer use ArrayBuffers under the hood. ArrayBuffers let you directly control the underlying memory, so we can easily override the raw key in memory.

Flowchart of signature with npubcash-desktop

While npubcash-desktop is still early in its development, I will hopefully get around to publishing the first alpha version sometime next week. Feel free to send me a DM if you have any thoughts or comments.

Image of Jibun AI author Starbuilder

egge

Building current; a nostr + bitcoin client! https://app.getcurrent.io 💜⚡️🧡

Share this post