Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NIP-49: Private key encryption #133

Merged
merged 23 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a6bd215
Key export/import as implemented by gossip
mikedilger Dec 30, 2022
5ed9ed3
Added test data
mikedilger Dec 30, 2022
c27e971
Last push didn't make sense, only this direction works
mikedilger Dec 30, 2022
b4fdff3
Multiple updates: 100k rounds, random salt, version number, length in…
mikedilger Jan 2, 2023
f2cdf3c
Rename to NIP-49, include in README
mikedilger Jan 14, 2023
fbfa2b4
Change encoding to bech32 (ncryptsec)
mikedilger Jan 14, 2023
6724dad
Merge remote-tracking branch 'origin/master' into nip-nn-key-export
ezicheq Jan 15, 2023
a078706
Major rework of the algorithm. NIP is now incomplete as I haven't cod…
ezicheq Jan 15, 2023
f4c698c
renamed
ezicheq Jan 15, 2023
7523836
spelling
ezicheq Jan 15, 2023
e10d394
minor fix
ezicheq Jan 15, 2023
ba7f14e
formatting
ezicheq Jan 15, 2023
6f15b96
MORE CHANGES: scrypt, spelt out more detail of the steps to take
mikedilger Jan 20, 2023
e06cb3e
Merge remote-tracking branch 'origin/master' into nip-nn-key-export
mikedilger Jan 20, 2023
e33105f
spelling
mikedilger Feb 3, 2023
885e40a
Mostly just removing some unnecesary stuff
mikedilger Jan 23, 2024
cf229dd
Remove the cafebabe note
mikedilger Jan 24, 2024
61b87e9
Remove confusing nonce statement
mikedilger Jan 24, 2024
836f476
Change title (and a bit of wording)
mikedilger Jan 25, 2024
177dbad
remove author
mikedilger Jan 25, 2024
5b003aa
remove legacy event
fiatjaf Jan 27, 2024
064f673
rename on README
fiatjaf Jan 27, 2024
586f9f5
Merge branch 'master' into nip-nn-key-export
fiatjaf Jan 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions 49.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@

NIP-49
======

Private Key Encryption
----------------------

`draft` `optional`

This NIP defines a method by which clients can encrypt (and decrypt) a user's private key with a passphrase.

Symmetric Encryption Key derivation
-----------------------------------

PASSPHRASE = read from the user

LOG\_N = Let the user or implementer choose one byte representing a power of 2 (e.g. 18 represents 262,144) which is used as the number of rounds for scrypt. Larger numbers take more time and more memory, and offer better protection:

| LOG\_N | MEMORY REQUIRED | APPROX TIME ON FAST COMPUTER |
|--------|-----------------|----------------------------- |
| 16 | 64 MiB | 100 ms |
| 18 | 256 MiB | |
| 20 | 1 GiB | 2 seconds |
| 21 | 2 GiB | |
| 22 | 4 GiB | |

SALT = 16 random bytes

SYMMETRIC_KEY = scrypt(passphrase=PASSPHRASE, salt=SALT, log\_n=LOG\_N, r=8, p=1)

The symmetric key should be 32 bytes long.

This symmetric encryption key is temporary and should be zeroed and discarded after use and not stored or reused for any other purpose.


Encrypting a private key
------------------------

The private key encryption process is as follows:

PRIVATE\_KEY = User's private (secret) secp256k1 key as 32 raw bytes (not hex or bech32 encoded!)

KEY\_SECURITY\_BYTE = one of:

* 0x00 - if the key has been known to have been handled insecurely (stored unencrypted, cut and paste unencrypted, etc)
* 0x01 - if the key has NOT been known to have been handled insecurely (stored unencrypted, cut and paste unencrypted, etc)
* 0x02 - if the client does not track this data

ASSOCIATED\_DATA = KEY\_SECURITY\_BYTE

NONCE = 24 byte random nonce

CIPHERTEXT = XChaCha20-Poly1305(
plaintext=PRIVATE\_KEY,
associated_data=ASSOCIATED\_DATA,
nonce=NONCE,
key=SYMMETRIC\_KEY
)

VERSION\_NUMBER = 0x02

CIPHERTEXT_CONCATENATION = concat(
VERSION\_NUMBER,
LOG\_N,
SALT,
NONCE,
ASSOCIATED\_DATA,
CIPHERTEXT
)

ENCRYPTED\_PRIVATE\_KEY = bech32_encode('ncryptsec', CIPHERTEXT\_CONCATENATION)

The output prior to bech32 encoding should be 91 bytes long.

The decryption process operates in the reverse.


Test Data
---------

The following encrypted private key:

`ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p`

When decrypted with password='nostr' and log_n=16 yields the following hex-encoded private key:

`3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683`

The reverse process is non-deterministic due to the random nonce.

Discussion
----------

### On Key Derivation

Passwords make poor cryptographic keys. Prior to use as a cryptographic key, two things need to happen:

1. An encryption key needs to be deterministically created from the password such that is has a uniform functionally random distribution of bits, such that the symmetric encryption algorithm's assumptions are valid, and
2. A slow irreversible algorithm should be injected into the process, so that brute-force attempts to decrypt by trying many passwords are severely hampered.

These are achieved using a password-based key derivation function. We use scrypt, which has been proven to be maximally memory hard and which several cryptographers have indicated to the author is better than argon2 even though argon2 won a competition in 2015.

### On the symmetric encryption algorithm

XChaCha20-Poly1305 is typically favored by cryptographers over AES and is less associated with the U.S. government. It (or it's earlier variant without the 'X') is gaining wide usage, is used in TLS and OpenSSH, and is available in most modern crypto libraries.

Recommendations
---------

It is not recommended that users publish these encrypted private keys to nostr, as cracking a key may become easier when an attacker can amass many encrypted private keys.

It is recommended that clients zero out the memory of passwords and private keys before freeing that memory.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ NIPs stand for **Nostr Implementation Possibilities**. They exist to document wh
- [NIP-36: Sensitive Content](36.md)
- [NIP-40: Expiration Timestamp](40.md)
- [NIP-42: Authentication of clients to relays](42.md)
- [NIP-49: Private Key Transfer](49.md)
fiatjaf marked this conversation as resolved.
Show resolved Hide resolved

## Event Kinds

Expand All @@ -48,6 +49,7 @@ NIPs stand for **Nostr Implementation Possibilities**. They exist to document wh
| 45-49 | Public Chat Reserved | [28](28.md) |
| 22242 | Client Authentication | [42](42.md) |
| 10000-19999 | Replaceable Events Reserved | [16](16.md) |
| 10002 | Private Key Transfer | [49](49.md) |
mikedilger marked this conversation as resolved.
Show resolved Hide resolved
fiatjaf marked this conversation as resolved.
Show resolved Hide resolved
| 20000-29999 | Ephemeral Events Reserved | [16](16.md) |


Expand Down