From fa64d49f2578cc2ab5cb277d09b5a8caa756c146 Mon Sep 17 00:00:00 2001 From: Pas <74743263+Pasithea0@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:42:34 -0600 Subject: [PATCH] Create derive-public-key.post.ts --- server/routes/auth/derive-public-key.post.ts | 46 ++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 server/routes/auth/derive-public-key.post.ts diff --git a/server/routes/auth/derive-public-key.post.ts b/server/routes/auth/derive-public-key.post.ts new file mode 100644 index 0000000..ed735b3 --- /dev/null +++ b/server/routes/auth/derive-public-key.post.ts @@ -0,0 +1,46 @@ +import { z } from 'zod'; +import { pbkdf2 } from 'crypto'; +import nacl from 'tweetnacl'; + +const requestSchema = z.object({ + mnemonic: z.string().min(1), +}); + +function toBase64Url(input: Uint8Array): string { + const base64 = Buffer.from(input).toString('base64'); + return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''); +} + +function pbkdf2Async(password: string, salt: string, iterations: number, keyLen: number, digest: string): Promise { + return new Promise((resolve, reject) => { + pbkdf2(password, salt, iterations, keyLen, digest, (err, derivedKey) => { + if (err) return reject(err); + resolve(new Uint8Array(derivedKey)); + }); + }); +} + +export default defineEventHandler(async (event) => { + const body = await readBody(event); + + const parsed = requestSchema.safeParse(body); + if (!parsed.success) { + throw createError({ + statusCode: 400, + message: 'Invalid request body', + }); + } + + const { mnemonic } = parsed.data; + + // PBKDF2 (HMAC-SHA256) -> 32-byte seed, iterations = 2048, salt = "mnemonic" + const seed = await pbkdf2Async(mnemonic, 'mnemonic', 2048, 32, 'sha256'); + + // Deterministic Ed25519 keypair from seed + const keyPair = nacl.sign.keyPair.fromSeed(seed); + const publicKeyBase64Url = toBase64Url(keyPair.publicKey); + + return { publicKey: publicKeyBase64Url }; +}); + +