minor edits to NIP-XX

This commit is contained in:
sshmatrix 2023-02-19 13:09:48 +05:30
parent 299c8b992e
commit b2a2711af1

72
xx.md
View File

@ -1,65 +1,64 @@
NIP-XX # NIP-XX
--
Nostr-Specific Deterministic Private Key Generation from Ethereum Wallet Signature Nostr-Specific Deterministic Private Key Generation from Ethereum Wallet Signature
-- --
`draft` `optional` `author:0xc0de4c0ffee` `author:sshmatrix` `draft` `optional` `author:0xc0de4c0ffee` `author:sshmatrix`
## Abstract ## Abstract
This specification provides an optional method for Nostr clients to generate deterministic private keys from Ethereum wallet signatures. This specification provides an optional method for Nostr clients to generate deterministic private keys from Ethereum wallet signatures. This NIP proposes HMAC Key Derivation Function (HKDF) coupled with SHA-256 from ECDSA signatures (EIP-191) as an alternative to the Schnorr signatures (BIP-340), allowing Nostr to interact with Ethereum ecosystem.
## Terminology ## Terminology
### a) `username` ### a) Username
Username can be either of the following: Username can be either of the following:
`username` or `user@domain.eth.limo` or `domain.eth.limo` or `sub.domain.eth.limo`, where, `petname` or `petname@domain.eth.limo` or `domain.eth.limo` or `sub.domain.eth.limo`, where
- `username` is a [NIP-02](https://github.com/nostr-protocol/nips/blob/master/05.md) compatible name, - `petname` is a NIP-02 compatible name,
- `name@domain.eth.limo` is a [NIP-05](https://github.com/nostr-protocol/nips/blob/master/05.md) compatible name, - `petname@domain.eth.limo` is a NIP-05 compatible name,
- `domain.eth.limo` is [NIP-05](https://github.com/nostr-protocol/nips/blob/master/05.md) equivalent of - `domain.eth.limo` is NIP-05 equivalent of `_@domain.eth.limo`,
`_@domain.eth.limo`, - `sub.domain.eth.limo` is NIP-05 equivalent of `_@sub.domain.eth.limo`.
- `sub.domain.eth.limo` is [NIP-05](https://github.com/nostr-protocol/nips/blob/master/05.md) equivalent of > a) `sub@domain.eth.limo` and `sub.domain.eth.limo` are NOT equivalent as their signatures will be different
`_@sub.domain.eth.limo`.
> Note : `sub@domain.eth.limo` and `sub.domain.eth.limo` are NOT same ID as their signatures will be different.
> b) `petname` can be the same as `domain`
### b) `password` ### b) Password
Password is optional string value used in HKDF salt, Password is an optional string value used in HKDF salt,
```js ```js
let password = "horse staple battery" let password = "horse staple battery"
let salt = await sha256(`eip155:${chainId}:${username}:${password?password:""}:${signature.slice(68)}`); let salt = await sha256(`eip155:${chainId}:${username}:${password?password:""}:${signature.slice(68)}`);
``` ```
### c) `message` ### c) Message
Message is text on screen that should warn the users to not sign messages indiscriminately,
```js ```js
let message = `Login to Nostr as ${username}\n\nImportant: Please verify the integrity and authenticity of your Nostr client before signing this message.\n${info}` let message = `Login to Nostr as ${username}\n\nImportant: Please verify the integrity and authenticity of your Nostr client before signing this message.\n${info}`
``` ```
### d) `signature` ### d) Signature
Deterministic signature from connected wallet. Signatures are 65 bytes long, `bytes1(v)+bytes32(r)+bytes32(s)`. Signature is the deterministic signature from connected Ethereum wallet. Ethereum signatures `(v,r,s)` are 65 bytes long, i.e. `bytes1(v) + bytes32(r) + bytes32(s)`,
```js ```js
let signature = wallet.signMessage(message); let signature = wallet.signMessage(message);
``` ```
### e) `HKDF` ### e) HKDF (HMAC Key Derivation Function)
HKDF-SHA-256 is used to derive 42 bytes long hash key. HKDF-SHA-256 is used to derive the 42 bytes long hash key: `hkdf(sha256, inputKey, salt, info, dkLen = 42)`
`hkdf(sha256, inputKey, salt, info, dkLen = 42)`
- `Input key` is SHA-256 hash of signature bytes. - `Input key` is SHA-256 hash of signature bytes.
```js ```js
let inputKey = await sha256(hexToBytes(signature.slice(2))); let inputKey = await sha256(hexToBytes(signature.slice(2)));
``` ```
- `Salt` is SHA-256 hash of following identifier string. `signature.slice(68)` is hex `s` value of signature, last 32 bytes. - `Salt` is SHA-256 hash of the following identifier string:
```js ```js
let salt = await sha256(`eip155:${chainId}:${username}:${password?password:""}:${signature.slice(68)}`); let salt = await sha256(`eip155:${chainId}:${username}:${password?password:""}:${signature.slice(68)}`);
``` ```
- `Info` is string with following format. where, `signature.slice(68)` is hex `s` value of Ethereum signature, i.e. the last 32 bytes.
- `Info` is a string with the following format:
```js ```js
let info = `eip155:${chainId}:${username}:${address}`; let info = `eip155:${chainId}:${username}:${address}`;
``` ```
- `Derived Key Length` is set to 42. FIPS 186/4 B.4.1 require hash length to be >=n+8 where, n is length of final private key. (42 >= 32 + 8) - Derived Key Length `dkLen` is set to 42.
```js ```js
let dkLen = 42; let dkLen = 42;
``` ```
- `hashToPrivateKey` function is FIPS 186-4 B.4.1 implementation to convert derived hash keys from `HKDF`to valid `secp256k1` private keys. This function is implemented in js `@noble/secp256k1` as `hashToPrivateKey`. FIPS 186/4 B.4.1 requires hash length to be ≥ n+8, where n is the length of final private key, such that 42 ≥ 32 + 8
- `hashToPrivateKey` function is FIPS 186-4 B.4.1 implementation to convert hash keys derived using HKDF to valid `secp256k1` private keys. This function is implemented in JavaScript library `@noble/secp256k1` as `hashToPrivateKey()`.
```js ```js
let hashKey = hkdf(sha256, inputKey, salt, info, dkLen=42); let hashKey = hkdf(sha256, inputKey, salt, info, dkLen=42);
let privKey = secp256k1.utils.hashToPrivateKey(hashKey); let privKey = secp256k1.utils.hashToPrivateKey(hashKey);
@ -68,21 +67,21 @@ HKDF-SHA-256 is used to derive 42 bytes long hash key.
## Implementation Requirements ## Implementation Requirements
- Connected Ethereum Wallet signer MUST be EIP191 and RFC6979 compatible. - Connected Ethereum wallet signer MUST be EIP191 and RFC6979 compatible.
- The message MUST be string formatted as `Login to Nostr as ${username}\n\nImportant: Please verify the integrity and authenticity of your Nostr client before signing this message.\n${info}`. - The message MUST be string formatted as `Login to Nostr as ${username}\n\nImportant: Please verify the integrity and authenticity of your Nostr client before signing this message.\n${info}`.
- HKDF input key MUST be generated as the SHA-256 hash of 65 bytes signature. - HKDF input key MUST be generated as the SHA-256 hash of 65 bytes signature.
- HKDF salt MUST be generated as SHA-256 hash of string `eip155:${chainID}:${username}:${password?password:""}:${signature.slice(68)}`. - HKDF salt MUST be generated as SHA-256 hash of string `eip155:${chainID}:${username}:${password?password:""}:${signature.slice(68)}`.
- HKDF derived key length MUST be 42. - HKDF derived key length MUST be 42.
- HKDF info MUST be string formatted as `eip155:${chainId}:${username}:${address}` - HKDF info MUST be string formatted as `eip155:${chainId}:${username}:${address}`.
## JS Example ## JS Example
```js ```js
const secp256k1 = require('@noble/secp256k1'); const secp256k1 = require('@noble/secp256k1');
const {hexToBytes, bytesToHex} = require('@noble/hashes/utils'); const {hexToBytes, bytesToHex} = require('@noble/hashes/utils');
const {hkdf} = require('@noble/hashes/hkdf'); const {hkdf} = require('@noble/hashes/hkdf');
const {sha256} = require('@noble/hashes/sha256'); const {sha256} = require('@noble/hashes/sha256');
// const wallet = // connected ethereum wallet with ethers.js // const wallet = connected ethereum wallet with ethers.js
let username = "me@domain.eth.limo" let username = "me@domain.eth.limo"
let chainId = wallet.getChainId(); // get chainid from connected wallet let chainId = wallet.getChainId(); // get chainid from connected wallet
let address = wallet.getAddress(); // get address from wallet let address = wallet.getAddress(); // get address from wallet
@ -94,7 +93,7 @@ let signature = wallet.signMessage(message); // request signature from wallet
let password = "horse staple battery" let password = "horse staple battery"
let inputKey = await sha256(hexToBytes(signature.slice(2))); //skip "0x" let inputKey = await sha256(hexToBytes(signature.slice(2))); //skip "0x"
let salt = await sha256(`eip155:${chainId}:${username}:${password?password:""}:${signature.slice(68)}`); let salt = await sha256(`eip155:${chainId}:${username}:${password?password:""}:${signature.slice(68)}`);
let dkLen = 42; let dkLen = 42;
let hashKey = await hkdf(sha256, inputKey, salt, info, dkLen); let hashKey = await hkdf(sha256, inputKey, salt, info, dkLen);
let privKey = secp256k1.utils.hashToPrivateKey(hashKey); let privKey = secp256k1.utils.hashToPrivateKey(hashKey);
@ -104,15 +103,16 @@ let pubKey = secp256k1.schnorr.getPublicKey(privKey);
## Security Considerations ## Security Considerations
- Users should always verify the integrity and authenticity of the Nostr client before signing the message. - Users should always verify the integrity and authenticity of the Nostr client before signing the message.
- Users should ensure that they only input their Nostr username and password in trusted and secure clients. - Users should ensure that they only input their Nostr Username and Password in trusted and secure clients.
- Implementing clients should ensure ~~..private key/security~~
## References: ## References:
- [RFC6979: Deterministic Usage of the DSA and ECDSA](https://datatracker.ietf.org/doc/html/rfc6979) - [RFC6979: Deterministic Usage of the DSA and ECDSA](https://datatracker.ietf.org/doc/html/rfc6979)
- [RFC5869: HKDF (HMAC-based Extract-and-Expand Key Derivation Function)](https://datatracker.ietf.org/doc/html/rfc5869) - [RFC5869: HKDF (HMAC-based Extract-and-Expand Key Derivation Function)](https://datatracker.ietf.org/doc/html/rfc5869)
- [Digital Signature Standard (DSS), FIPS 186-4 B.4.1](https://csrc.nist.gov/publications/detail/fips/186/4/final) - [Digital Signature Standard (DSS), FIPS 186-4 B.4.1](https://csrc.nist.gov/publications/detail/fips/186/4/final)
- [BIP340: Schnorr Signature Standard](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki)
- [ERC191: Signed Data Standard](https://eips.ethereum.org/EIPS/eip-191) - [ERC191: Signed Data Standard](https://eips.ethereum.org/EIPS/eip-191)
- [EIP155: Simple replay attack protection](https://eips.ethereum.org/EIPS/eip-155) - [EIP155: Simple replay attack protection](https://eips.ethereum.org/EIPS/eip-155)
- [NIP-02: Contact List and Petnames](https://github.com/nostr-protocol/nips/blob/master/02.md)
- [NIP-05: Mapping Nostr keys to DNS-based internet identifiers](https://github.com/nostr-protocol/nips/blob/master/05.md)
- [@noble/hashes](https://github.com/paulmillr/noble-hashes) - [@noble/hashes](https://github.com/paulmillr/noble-hashes)
- [@noble/secp256k1](https://github.com/paulmillr/noble-secp256k1) - [@noble/secp256k1](https://github.com/paulmillr/noble-secp256k1)