diff --git a/src/components/ConfigContext.tsx b/src/components/ConfigContext.tsx
index 6b0aca9..50cdfdd 100644
--- a/src/components/ConfigContext.tsx
+++ b/src/components/ConfigContext.tsx
@@ -226,15 +226,31 @@ function ConfigPopover({ config, setConfig, onClose, anchorRef, onOpenKeyModal }
);
}
-function validateMeshcoreKey(base64Key: string): string | null {
- if (!base64Key) return null;
+// Add a helper to decode base64 or hex
+function decodeKeyString(key: string): Buffer | null {
+ if (!key) return null;
+ // Try base64 first
try {
- const bytes = Buffer.from(base64Key, 'base64');
- if (bytes.length !== 16) {
- return 'Key must decode to exactly 16 bytes.';
- }
- } catch {
- return 'Invalid base64 encoding.';
+ const b = Buffer.from(key, 'base64');
+ if (b.length === 16) return b;
+ } catch {}
+ // Try hex (with or without 0x)
+ let hex = key.trim();
+ if (hex.startsWith('0x')) hex = hex.slice(2);
+ if (/^[0-9a-fA-F]{32}$/.test(hex)) {
+ try {
+ const b = Buffer.from(hex, 'hex');
+ if (b.length === 16) return b;
+ } catch {}
+ }
+ return null;
+}
+
+function validateMeshcoreKey(key: string): string | null {
+ if (!key) return null;
+ const decoded = decodeKeyString(key);
+ if (!decoded) {
+ return 'Key must be 16 bytes, in base64 or hex (32 hex digits)';
}
return null;
}
@@ -263,7 +279,17 @@ function MeshcoreKeyModal({ config, setConfig, onClose }: { config: Config, setC
disabled
readOnly
/>
- ID: {getChannelIdFromKey(PUBLIC_MESHCORE_KEY.privateKey)}
+ {/* Only show ID if valid */}
+ {(() => {
+ try {
+ const id = getChannelIdFromKey(PUBLIC_MESHCORE_KEY.privateKey);
+ return (
+ ID: {id}
+ );
+ } catch {
+ return null;
+ }
+ })()}
- ID: {getChannelIdFromKey(key.privateKey)}
+ {/* Only show ID if valid */}
+ {(() => {
+ try {
+ const id = getChannelIdFromKey(key.privateKey);
+ return (
+ ID: {id}
+ );
+ } catch {
+ return null;
+ }
+ })()}
{
const updated = [...(config.meshcoreKeys || [])];
diff --git a/src/lib/meshcore.ts b/src/lib/meshcore.ts
index 899667d..e6a91cb 100644
--- a/src/lib/meshcore.ts
+++ b/src/lib/meshcore.ts
@@ -3,16 +3,35 @@ import { createHash } from "crypto";
// Module-level cache for channel IDs
const channelIdCache: Record = {};
+// Add a helper to decode base64 or hex
+function decodeKeyString(key: string): Buffer {
+ // Try base64 first
+ try {
+ const b = Buffer.from(key, 'base64');
+ if (b.length === 16) return b;
+ } catch {}
+ // Try hex (with or without 0x)
+ let hex = key.trim();
+ if (hex.startsWith('0x')) hex = hex.slice(2);
+ if (/^[0-9a-fA-F]{32}$/.test(hex)) {
+ try {
+ const b = Buffer.from(hex, 'hex');
+ if (b.length === 16) return b;
+ } catch {}
+ }
+ throw new Error('Invalid key format: must be 16 bytes, base64 or hex');
+}
+
/**
* Returns the channel id for a given base64-encoded key.
* Decodes the key, hashes it with SHA-256, and returns the first byte as hex.
* Results are cached for performance.
*/
-export function getChannelIdFromKey(base64Key: string): string {
- if (channelIdCache[base64Key]) return channelIdCache[base64Key];
- const keyBytes = Buffer.from(base64Key, 'base64');
+export function getChannelIdFromKey(key: string): string {
+ if (channelIdCache[key]) return channelIdCache[key];
+ const keyBytes = decodeKeyString(key);
const hash = createHash('sha256').update(keyBytes).digest();
const id = hash[0].toString(16).padStart(2, '0');
- channelIdCache[base64Key] = id;
+ channelIdCache[key] = id;
return id;
}
\ No newline at end of file
diff --git a/src/lib/meshcore_decrypt.ts b/src/lib/meshcore_decrypt.ts
index 7ac839c..88b08ad 100644
--- a/src/lib/meshcore_decrypt.ts
+++ b/src/lib/meshcore_decrypt.ts
@@ -17,6 +17,28 @@ function base64ToBytes(b64: string): Uint8Array {
return Uint8Array.from(atob(b64), c => c.charCodeAt(0));
}
+// Add a helper to decode base64 or hex
+function decodeKeyString(key: string): Uint8Array {
+ // Try base64 first
+ try {
+ const b = Uint8Array.from(atob(key), c => c.charCodeAt(0));
+ if (b.length === 16) return b;
+ } catch {}
+ // Try hex (with or without 0x)
+ let hex = key.trim();
+ if (hex.startsWith('0x')) hex = hex.slice(2);
+ if (/^[0-9a-fA-F]{32}$/.test(hex)) {
+ try {
+ const b = new Uint8Array(hex.length / 2);
+ for (let i = 0; i < hex.length; i += 2) {
+ b[i / 2] = parseInt(hex.slice(i, i + 2), 16);
+ }
+ if (b.length === 16) return b;
+ } catch {}
+ }
+ throw new Error('Invalid key format: must be 16 bytes, base64 or hex');
+}
+
// Helper: HMAC-SHA256, returns Uint8Array
async function hmacSha256(key: Uint8Array, data: Uint8Array): Promise {
if (window.crypto?.subtle) {
@@ -89,7 +111,7 @@ export async function decryptMeshcoreGroupMessage({
for (const base64Key of knownKeys) {
let keyBytes: Uint8Array;
try {
- keyBytes = base64ToBytes(base64Key);
+ keyBytes = decodeKeyString(base64Key);
} catch (e) {
console.warn("Skipping invalid base64 meshcore key:", base64Key, e);
failures.push({ key: base64Key, reason: `base64 decode error: ${e}` });