Enable recovery with a mnemonic seed per nip06
This commit is contained in:
parent
710a7dd2de
commit
ac444ed562
@ -97,6 +97,11 @@ export const EmailRegex =
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
|
||||
/**
|
||||
* Regex to match a mnemonic seed
|
||||
*/
|
||||
export const MnemonicRegex = /^([^\s]+\s){11}[^\s]+$/;
|
||||
|
||||
/**
|
||||
* Extract file extensions regex
|
||||
*/
|
||||
|
@ -5,14 +5,11 @@ import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import * as secp from "@noble/secp256k1";
|
||||
import { useIntl, FormattedMessage } from "react-intl";
|
||||
import { HDKey } from "@scure/bip32";
|
||||
import { wordlist } from "@scure/bip39/wordlists/english";
|
||||
import * as bip39 from "@scure/bip39";
|
||||
|
||||
import { RootState } from "State/Store";
|
||||
import { setPrivateKey, setPublicKey, setRelays, setGeneratedPrivateKey } from "State/Login";
|
||||
import { DefaultRelays, EmailRegex, DerivationPath } from "Const";
|
||||
import { bech32ToHex, unwrap } from "Util";
|
||||
import { DefaultRelays, EmailRegex, MnemonicRegex } from "Const";
|
||||
import { bech32ToHex, generateBip39Entropy, entropyToDerivedKey, unwrap } from "Util";
|
||||
import { HexKey } from "@snort/nostr";
|
||||
import ZapButton from "Element/ZapButton";
|
||||
// import useImgProxy from "Feed/ImgProxy";
|
||||
@ -100,12 +97,14 @@ export default function LoginPage() {
|
||||
} else if (key.match(EmailRegex)) {
|
||||
const hexKey = await getNip05PubKey(key);
|
||||
dispatch(setPublicKey(hexKey));
|
||||
} else if (key.match(MnemonicRegex)) {
|
||||
const ent = generateBip39Entropy(key);
|
||||
const keyHex = entropyToDerivedKey(ent);
|
||||
dispatch(setPrivateKey(keyHex));
|
||||
} else if (secp.utils.isValidPrivateKey(key)) {
|
||||
dispatch(setPrivateKey(key));
|
||||
} else {
|
||||
if (secp.utils.isValidPrivateKey(key)) {
|
||||
dispatch(setPrivateKey(key));
|
||||
} else {
|
||||
throw new Error("INVALID PRIVATE KEY");
|
||||
}
|
||||
throw new Error("INVALID PRIVATE KEY");
|
||||
}
|
||||
} catch (e) {
|
||||
setError(`Failed to load NIP-05 pub key (${e})`);
|
||||
@ -114,17 +113,9 @@ export default function LoginPage() {
|
||||
}
|
||||
|
||||
async function makeRandomKey() {
|
||||
const mn = bip39.generateMnemonic(wordlist);
|
||||
const ent = bip39.mnemonicToEntropy(mn, wordlist);
|
||||
const ent = generateBip39Entropy();
|
||||
const entHex = secp.utils.bytesToHex(ent);
|
||||
const masterKey = HDKey.fromMasterSeed(ent);
|
||||
const newKey = masterKey.derive(DerivationPath);
|
||||
|
||||
if (!newKey.privateKey) {
|
||||
throw new Error("INVALID PRIVATE KEY DERIVATION");
|
||||
}
|
||||
|
||||
const newKeyHex = secp.utils.bytesToHex(newKey.privateKey);
|
||||
const newKeyHex = entropyToDerivedKey(ent);
|
||||
dispatch(setGeneratedPrivateKey({ key: newKeyHex, entropy: entHex }));
|
||||
navigate("/new");
|
||||
}
|
||||
|
@ -46,5 +46,5 @@ export default defineMessages({
|
||||
},
|
||||
Bookmarks: { defaultMessage: "Bookmarks" },
|
||||
BookmarksCount: { defaultMessage: "{n} Bookmarks" },
|
||||
KeyPlaceholder: { defaultMessage: "nsec, npub, nip-05, hex" },
|
||||
KeyPlaceholder: { defaultMessage: "nsec, npub, nip-05, hex, mnemonic" },
|
||||
});
|
||||
|
@ -7,7 +7,9 @@ import base32Decode from "base32-decode";
|
||||
import { HexKey, TaggedRawEvent, u256, EventKind, encodeTLV, NostrPrefix } from "@snort/nostr";
|
||||
import * as bip39 from "@scure/bip39";
|
||||
import { wordlist } from "@scure/bip39/wordlists/english";
|
||||
import { HDKey } from "@scure/bip32";
|
||||
|
||||
import { DerivationPath } from "Const";
|
||||
import { MetadataCache } from "State/Users";
|
||||
|
||||
export const sha256 = (str: string) => {
|
||||
@ -102,6 +104,15 @@ export function hexToBech32(hrp: string, hex?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function generateBip39Entropy(mnemonic?: string): Uint8Array {
|
||||
try {
|
||||
const mn = mnemonic ?? bip39.generateMnemonic(wordlist);
|
||||
return bip39.mnemonicToEntropy(mn, wordlist);
|
||||
} catch (e) {
|
||||
throw new Error("INVALID MNEMONIC PHRASE");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hex-encoded entropy into mnemonic phrase
|
||||
*/
|
||||
@ -110,6 +121,22 @@ export function hexToMnemonic(hex: string): string {
|
||||
return bip39.entropyToMnemonic(bytes, wordlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert mnemonic phrase into hex-encoded private key
|
||||
* using the derivation path specified in NIP06
|
||||
* @param mnemonic the mnemonic-encoded entropy
|
||||
*/
|
||||
export function entropyToDerivedKey(entropy: Uint8Array): string {
|
||||
const masterKey = HDKey.fromMasterSeed(entropy);
|
||||
const newKey = masterKey.derive(DerivationPath);
|
||||
|
||||
if (!newKey.privateKey) {
|
||||
throw new Error("INVALID KEY DERIVATION");
|
||||
}
|
||||
|
||||
return secp.utils.bytesToHex(newKey.privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hex pubkey to bech32 link url
|
||||
*/
|
||||
|
@ -197,9 +197,6 @@
|
||||
"B6+XJy": {
|
||||
"defaultMessage": "zapped"
|
||||
},
|
||||
"B6H7eJ": {
|
||||
"defaultMessage": "nsec, npub, nip-05, hex"
|
||||
},
|
||||
"BOUMjw": {
|
||||
"defaultMessage": "No nostr users found for {twitterUsername}"
|
||||
},
|
||||
@ -507,6 +504,9 @@
|
||||
"WxthCV": {
|
||||
"defaultMessage": "e.g. Jack"
|
||||
},
|
||||
"X7xU8J": {
|
||||
"defaultMessage": "nsec, npub, nip-05, hex, mnemonic"
|
||||
},
|
||||
"XgWvGA": {
|
||||
"defaultMessage": "Reactions"
|
||||
},
|
||||
|
@ -63,7 +63,6 @@
|
||||
"AyGauy": "Login",
|
||||
"B4C47Y": "name too short",
|
||||
"B6+XJy": "zapped",
|
||||
"B6H7eJ": "nsec, npub, nip-05, hex",
|
||||
"BOUMjw": "No nostr users found for {twitterUsername}",
|
||||
"BOr9z/": "Snort is an open source project built by passionate people in their free time",
|
||||
"BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.",
|
||||
@ -164,6 +163,7 @@
|
||||
"W9355R": "Unmute",
|
||||
"WONP5O": "Find your twitter follows on nostr (Data provided by {provider})",
|
||||
"WxthCV": "e.g. Jack",
|
||||
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
|
||||
"XgWvGA": "Reactions",
|
||||
"Y31HTH": "Help fund the development of Snort",
|
||||
"YDURw6": "Service URL",
|
||||
|
Loading…
x
Reference in New Issue
Block a user