nostr-protocol / nips

Nostr Implementation Possibilities
2.33k stars 565 forks source link

NSEC/NPUB Key storage, what's the correct way? #839

Open ghost opened 11 months ago

ghost commented 11 months ago

I am currently working on an application that generates public/private keypairs, and I was wondering what the correct way to store them is. So far I haven't seen any issues on this subject, so I opened one.

How is everybody storing their keys, in plain public.npub and private.nsec files, or maybe public.key and private.key?

I think picking a single extension and format would the best, because now people just do whatever they think is best which can cause some confusion.

ghost commented 11 months ago

I found https://github.com/nostr-protocol/nips/issues/312, but that issue is more for the secure storing/exporting of the keys (by utilizing encryption), but not about just storing them

fiatjaf commented 11 months ago

You mean for desktop clients? There is no standard format. But there are not many desktop clients, so we could create a standard right now and I think it would be adopted and a good thing. What do you think, @mikedilger @reyamir?

ghost commented 11 months ago

Not just desktop clients, as I think a lot of other things would benefit from this aswell. But yeah, the sooner the better!

What do you think of this:

By allowing an user to pick a custom filename we don't run into any issues where a program needs to create many different keyfiles and puts them into a single directory. This can also be useful to "seperate" certain private keys, for example; a file named profile1.nsec and profile2.nsec would be cleaner than having to create 2 new directories and put the keyfiles in there.

I haven't really thought the actual content format out, though. Perhaps we could store the keys in a PEM like format, like:

-----BEGIN NSEC-----
private key in hex encoded format
-----END NSEC-----

If we use this format, then maybe we can drop the different extensions and use a single one, as keys are identified by their type with the BEGIN <format> marker. This could make adding encrypted keyfiles easier too:

-----BEGIN NCRYPTSEC-----
private key in hex encoded format, encrypted using AES-256 (or something like that)
-----END NCRYPTSEC-----
mikedilger commented 11 months ago

If the extensions are going to be .nsec and .npub I don't think we need armor. Just put the nsec or npub in the file without even a linefeed or carriage return (although software should be robust and strip white space).

I opened https://github.com/nostr-protocol/nips/pull/133 ages ago. The commentary is all over the place since I made major changes right before the last few comments. In any case, gossip uses this to store private keys since I don't think private keys should ever be in plaintext (on disk, displayed to the user, etc) except when in working memory so they can be used... and even in that case the memory should be zeroed before it is deallocated. Of course a root user can look at /dev/kmem and see the naked private key, and rowhammer attacks could probably find it. But at least non-root users can't, and casual leakage is much less likely.

ghost commented 11 months ago

I do think having some sort of "armor" to protect keys would be very useful, and I think the PEM format would be the best way forward. We can expand it later on by adding metadata (like used cipher, hashing algorithm, version info, whatever). This makes it also easier to add new types later on, if needed.

But having extensions and armor together seems a bit wasteful, perhaps a single extension like ".nostr" and the armor would do the job?

fiatjaf commented 11 months ago

No armors, that's unnecessary bloat and "flexibility" that we don't want because it benefits no one. We pick a standard and everybody uses it. If we ever have to change that, hopefully never, we can make a new filename or something like that.

Do you think having an encrypted private key in a specific file at a determinate location in the filesystem that multiple Nostr clients can use is a good idea, @mikedilger? Do you have that figured out for Gossip that we can copy (hopefully it's not an encryption standard that is absurdly hard to implement and depend on Rust-only libraries)?

reyamir commented 11 months ago

I don't have much knowledge in the security field to jump into the conversation, but as a user, I prefer to be able to choose a folder to store the encrypted private key beside the default folder.

like generate ssh-key, default folder is ~/.ssh, with nostr key we have ~/.nostr, or user can choose whatever folder they want

I think we only need to store nsec key as <account1>.nsec, no need to store npub

Then we can ux flow like this for desktop client:

Lume -> Export private key -> Save encrypted key in default folder / or user chosed folder
Gossip -> User import privkey / or Gossip can auto discovery nostr account by looking on ~/.nostr folder
mikedilger commented 11 months ago

No armors, that's unnecessary bloat and "flexibility" that we don't want because it benefits no one.

I agree, I don't see the benefit, I just see downside (more shit to parse).

Do you think having an encrypted private key in a specific file at a determinate location in the filesystem that multiple Nostr clients can use is a good idea, @mikedilger? Do you have that figured out for Gossip that we can copy (hopefully it's not an encryption standard that is absurdly hard to implement and depend on Rust-only libraries)?

I don't store it in a well known file. Interesting idea though.

The encryption is basically: symmetric_key = scrypt(password) raw_encrypted = xchacha20-poly1305(symmetric_key, private_key) final_encrypted = concatenate nonce and stuff, and bech32 encode it

scrypt, xchacha20-poly1305, and bech32 should all be common enough.

ghost commented 11 months ago

like generate ssh-key, default folder is ~/.ssh, with nostr key we have ~/.nostr, or user can choose whatever folder they want

I am not really a fan of putting junk into an users home directory, why not follow the XDG spec?

I think this works best:

  1. Check if $XDG_CONFIG_DIR/nostr exists, and if it does use that, if not we continue
  2. Check if $HOME/.config/nostr exists, and if it does use that, if not we continue
  3. Create $HOME/.config/nostr and use that

Maybe an environment variable would be interesting aswell?

Links to the XDG specification: