diff --git a/packages/app/package.json b/packages/app/package.json
index 5c2f67bf..2606fa5f 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -12,6 +12,8 @@
"@noble/secp256k1": "^1.7.0",
"@protobufjs/base64": "^1.1.2",
"@reduxjs/toolkit": "^1.9.1",
+ "@scure/bip32": "^1.1.5",
+ "@scure/bip39": "^1.1.1",
"@snort/nostr": "^1.0.0",
"@szhsin/react-menu": "^3.3.1",
"base32-decode": "^1.0.0",
diff --git a/packages/app/src/Const.ts b/packages/app/src/Const.ts
index 738f8fbd..9ac753e7 100644
--- a/packages/app/src/Const.ts
+++ b/packages/app/src/Const.ts
@@ -85,6 +85,11 @@ export const ZapperSpam = [
"e1ff3bfdd4e40315959b08b4fcc8245eaa514637e1d4ec2ae166b743341be1af", // benthecarman
];
+/**
+ * NIP06-defined derivation path for private keys
+ */
+export const DerivationPath = "m/44'/1237'/0'/0/0";
+
/**
* Regex to match email address
*/
diff --git a/packages/app/src/Pages/Login.tsx b/packages/app/src/Pages/Login.tsx
index bc2548a4..8f303f7a 100644
--- a/packages/app/src/Pages/Login.tsx
+++ b/packages/app/src/Pages/Login.tsx
@@ -5,10 +5,13 @@ 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 } from "Const";
+import { DefaultRelays, EmailRegex, DerivationPath } from "Const";
import { bech32ToHex, unwrap } from "Util";
import { HexKey } from "@snort/nostr";
import ZapButton from "Element/ZapButton";
@@ -111,8 +114,18 @@ export default function LoginPage() {
}
async function makeRandomKey() {
- const newKey = secp.utils.bytesToHex(secp.utils.randomPrivateKey());
- dispatch(setGeneratedPrivateKey(newKey));
+ const mn = bip39.generateMnemonic(wordlist);
+ const ent = bip39.mnemonicToEntropy(mn, wordlist);
+ 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);
+ dispatch(setGeneratedPrivateKey({ key: newKeyHex, entropy: entHex }));
navigate("/new");
}
diff --git a/packages/app/src/Pages/new/NewUserFlow.tsx b/packages/app/src/Pages/new/NewUserFlow.tsx
index 96f21eaf..6e5b3e80 100644
--- a/packages/app/src/Pages/new/NewUserFlow.tsx
+++ b/packages/app/src/Pages/new/NewUserFlow.tsx
@@ -6,7 +6,7 @@ import Logo from "Element/Logo";
import { CollapsedSection } from "Element/Collapsed";
import Copy from "Element/Copy";
import { RootState } from "State/Store";
-import { hexToBech32 } from "Util";
+import { hexToBech32, hexToMnemonic } from "Util";
import messages from "./messages";
@@ -68,7 +68,7 @@ const Extensions = () => {
};
export default function NewUserFlow() {
- const { publicKey, privateKey } = useSelector((s: RootState) => s.login);
+ const { publicKey, privateKey, generatedEntropy } = useSelector((s: RootState) => s.login);
const navigate = useNavigate();
return (
@@ -91,6 +91,10 @@ export default function NewUserFlow() {
+
+
+
+