diff --git a/bun.lockb b/bun.lockb index a3d38b2..02c1670 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/Modals/Login.tsx b/components/Modals/Login.tsx index 5dc402c..9032f40 100644 --- a/components/Modals/Login.tsx +++ b/components/Modals/Login.tsx @@ -12,6 +12,7 @@ import { Label } from "@/components/ui/label"; import AddPassphrase from "./AddPassphrase"; import { decryptMessage } from "@/lib/nostr"; import { toast } from "sonner"; +import { generateSchnorrKeyPair } from "@/lib/keys"; export default function LoginModal() { const { loginWithNip07, loginWithSecret } = useNDK(); @@ -19,8 +20,10 @@ export default function LoginModal() { const [isLoading, setIsLoading] = useState(false); const [showExtensionLogin, setShowExtensionLogin] = useState(true); const [showPassphraseLogin, setShowPassphraseLogin] = useState(false); + const [showEmailLogin, setShowEmailLogin] = useState(false); const [nsec, setNsec] = useState(""); const [passphrase, setPassphrase] = useState(""); + const [email, setEmail] = useState(""); const [encryptedNsec, setEncryptedNsec] = useState(""); const modal = useModal(); @@ -92,6 +95,25 @@ export default function LoginModal() { setIsLoading(false); modal?.hide(); } + async function handleLoginEmail() { + if (!email || !passphrase) return; + setIsLoading(true); + const seedString = email + passphrase; + const { privateKey, publicKey } = generateSchnorrKeyPair(seedString); + console.log("privateKey", privateKey, "publicKey", publicKey); + const emailNsec = nip19.nsecEncode(privateKey); + const user = await loginWithSecret(emailNsec); + console.log("User", user); + if (!user) { + throw new Error("NO auth"); + } + await loginWithPubkey(nip19.decode(user.npub).data.toString()); + if (typeof window.webln !== "undefined") { + await window.webln.enable(); + } + setIsLoading(false); + modal?.hide(); + } async function handleLoginPassphrase() { if (!encryptedNsec || !passphrase) return; setIsLoading(true); @@ -138,7 +160,51 @@ export default function LoginModal() { Connect with extension )} - {showPassphraseLogin ? ( + {showEmailLogin ? ( +
+
+ + setEmail(e.target.value)} + placeholder="email" + type="email" + className="text-[16px]" + /> +
+
+ + setPassphrase(e.target.value)} + placeholder="password..." + type="password" + className="text-[16px]" + /> +
+ +
+ +
+
+ ) : showPassphraseLogin ? (
Connect with Nsec - {!!encryptedNsec && ( -
- -
- )} + +
+ Or, use + {!!encryptedNsec && ( + <> + + or + + )} + +
)} diff --git a/lib/hooks/useKeyboardShortcut.ts b/lib/hooks/useKeyboardShortcut.ts index d8819d6..72ea108 100644 --- a/lib/hooks/useKeyboardShortcut.ts +++ b/lib/hooks/useKeyboardShortcut.ts @@ -12,7 +12,7 @@ export const useKeyboardShortcut = (keys: Key[], callback: () => void) => { (key === "ctrl" && (event.metaKey || event.ctrlKey)) || (key === "shift" && event.shiftKey) || (key === "alt" && event.altKey) || - (typeof key === "string" && event.key.toLowerCase() === key), + (typeof key === "string" && event.key?.toLowerCase() === key), ) ) { callback(); diff --git a/lib/keys/index.ts b/lib/keys/index.ts new file mode 100644 index 0000000..555192e --- /dev/null +++ b/lib/keys/index.ts @@ -0,0 +1,24 @@ +import { createHmac } from "crypto"; +import { ec as EC } from "elliptic"; + +export function generateSchnorrKeyPair(seed: string): { + privateKey: string; + publicKey: string; +} { + // Use HMAC-SHA256 with your seed as the message and a secret key (salt) + const hmac = createHmac("sha256", "secret-salt"); + hmac.update(seed); + const derivedKey = hmac.digest(); + + // Create a key pair using the derived private key + const ec = new EC("secp256k1"); + const keyPair = ec.keyFromPrivate(derivedKey); + + // Get the public key in hexadecimal format + const publicKey = keyPair.getPublic("hex"); + + return { + privateKey: derivedKey.toString("hex"), + publicKey, + }; +} diff --git a/package.json b/package.json index 3b3371e..f83c2f5 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "dayjs": "^1.11.10", "dexie": "^3.2.4", "dexie-react-hooks": "^1.1.6", + "elliptic": "^6.5.4", "focus-trap-react": "^10.2.3", "framer-motion": "^10.16.4", "jotai": "^2.4.3", @@ -75,6 +76,7 @@ "@tailwindcss/typography": "^0.5.10", "@total-typescript/ts-reset": "^0.5.1", "@types/crypto-js": "^4.1.2", + "@types/elliptic": "^6.4.16", "@types/latlon-geohash": "^2.0.2", "@types/node": "^20", "@types/ramda": "^0.29.6",