From ea32ccea7700275414eb13ebf8a5d1c1cc50bc10 Mon Sep 17 00:00:00 2001 From: ajvpot <553597+ajvpot@users.noreply.github.com> Date: Mon, 15 Sep 2025 23:03:29 +0200 Subject: [PATCH] support # channels --- src/components/ConfigContext.tsx | 18 +++++++++++++++--- src/lib/meshcore.ts | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/components/ConfigContext.tsx b/src/components/ConfigContext.tsx index 1a8d75d..45a31a1 100644 --- a/src/components/ConfigContext.tsx +++ b/src/components/ConfigContext.tsx @@ -1,6 +1,6 @@ "use client"; import React, { createContext, useContext, useState, useEffect, useRef, useLayoutEffect, ReactNode } from "react"; -import { getChannelIdFromKey } from "@/lib/meshcore"; +import { getChannelIdFromKey, deriveKeyFromChannelName } from "@/lib/meshcore"; import { getRegionFriendlyNames } from "@/lib/regions"; import Modal from "./Modal"; @@ -353,7 +353,19 @@ function MeshcoreKeyModal({ config, setConfig, onClose }: { config: Config, setC value={key.channelName} onChange={e => { const updated = [...(config.meshcoreKeys || [])]; - updated[idx] = { ...updated[idx], channelName: e.target.value }; + const newChannelName = e.target.value; + const updatedKey = { ...updated[idx], channelName: newChannelName }; + + // Auto-populate private key for # channels + if (newChannelName.startsWith('#') && newChannelName.length > 1) { + try { + updatedKey.privateKey = deriveKeyFromChannelName(newChannelName); + } catch (error) { + // If derivation fails, leave the key as is + } + } + + updated[idx] = updatedKey; setConfig({ ...config, meshcoreKeys: updated }); }} /> @@ -373,7 +385,7 @@ function MeshcoreKeyModal({ config, setConfig, onClose }: { config: Config, setC { const updated = [...(config.meshcoreKeys || [])]; diff --git a/src/lib/meshcore.ts b/src/lib/meshcore.ts index cb2fee1..fa20fe3 100644 --- a/src/lib/meshcore.ts +++ b/src/lib/meshcore.ts @@ -1,6 +1,31 @@ import { createHash, createHmac } from "crypto"; import aesjs from "aes-js"; +/** + * Derives a 128-bit encryption key from a channel name that starts with '#'. + * Applies filtering: converts to lowercase and keeps only a-z, 0-9, and hyphen. + * Uses SHA256 hash of the filtered channel name (including '#') as ASCII bytes, + * then truncates to first 128 bits (16 bytes) and returns as hex. + */ +export function deriveKeyFromChannelName(channelName: string): string { + if (!channelName.startsWith('#')) { + throw new Error('Channel name must start with #'); + } + + // Apply filtering: lowercase and keep only a-z, 0-9, hyphen + const filteredName = '#' + channelName.slice(1) + .toLowerCase() + .replace(/[^a-z0-9-]/g, ''); + + // Convert filtered channel name to ASCII bytes and hash with SHA256 + const nameBytes = Buffer.from(filteredName, 'ascii'); + const hash = createHash('sha256').update(nameBytes).digest(); + + // Truncate to first 128 bits (16 bytes) and return as hex + const key128bit = hash.slice(0, 16); + return key128bit.toString('hex'); +} + // Module-level cache for channel IDs const channelIdCache: Record = {};