From ccdf167daf3da8504e9855f1866b58ef1d952822 Mon Sep 17 00:00:00 2001 From: nav Date: Thu, 16 Apr 2026 10:36:55 +0000 Subject: [PATCH] feat: add crypto module (Ed25519 key generation, sign, verify) Moves verifySignature, signMessage, generateKeyPair and utility functions from Yonexus.Client into Protocol so Server no longer depends on Client at build time or runtime. --- src/crypto.ts | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 + 2 files changed, 50 insertions(+) create mode 100644 src/crypto.ts diff --git a/src/crypto.ts b/src/crypto.ts new file mode 100644 index 0000000..ae900ff --- /dev/null +++ b/src/crypto.ts @@ -0,0 +1,49 @@ +import { randomBytes, generateKeyPair as _generateKeyPair, sign, verify } from "node:crypto"; + +export interface KeyPair { + readonly privateKey: string; + readonly publicKey: string; + readonly algorithm: "Ed25519"; +} + +export async function generateKeyPair(): Promise { + const { publicKey, privateKey } = await new Promise<{ publicKey: string; privateKey: string }>((resolve, reject) => { + _generateKeyPair( + "ed25519", + { + publicKeyEncoding: { type: "spki", format: "pem" }, + privateKeyEncoding: { type: "pkcs8", format: "pem" }, + }, + (err, pubKey, privKey) => (err ? reject(err) : resolve({ publicKey: pubKey, privateKey: privKey })) + ); + }); + return { privateKey, publicKey, algorithm: "Ed25519" }; +} + +export async function signMessage(privateKeyPem: string, message: Buffer | string): Promise { + return sign(null, typeof message === "string" ? Buffer.from(message) : message, privateKeyPem).toString("base64"); +} + +export async function verifySignature(publicKeyPem: string, message: Buffer | string, signature: string): Promise { + try { + return verify(null, typeof message === "string" ? Buffer.from(message) : message, publicKeyPem, Buffer.from(signature, "base64")); + } catch { + return false; + } +} + +export function generatePairingCode(): string { + const bytes = randomBytes(8); + const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; + let code = ""; + for (let i = 0; i < 12; i++) code += chars[bytes[i % bytes.length] % chars.length]; + return `${code.slice(0, 4)}-${code.slice(4, 8)}-${code.slice(8, 12)}`; +} + +export function generateSecret(): string { + return randomBytes(32).toString("base64url"); +} + +export function generateNonce(): string { + return randomBytes(18).toString("base64url").slice(0, 24); +} diff --git a/src/index.ts b/src/index.ts index b910275..c3d15f9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,4 @@ export * from "./types.js"; export * from "./codec.js"; export * from "./errors.js"; export * from "./auth.js"; +export * from "./crypto.js";