Message verification

This commit is contained in:
2026-08-22 10:58:34 +02:00
parent 8b80ecb8a8
commit 21261098dc
+28 -7
View File
@@ -7,6 +7,7 @@ import { ClientMeta, StoredMessage } from "@/lib/types";
import { base58 } from "@scure/base"; import { base58 } from "@scure/base";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import * as ed from "@noble/ed25519"; import * as ed from "@noble/ed25519";
import { CircleXIcon } from "lucide-react";
export default function TextChannel({ export default function TextChannel({
appRef, appRef,
@@ -64,13 +65,13 @@ export default function TextChannel({
</header> </header>
<div className="h-full flex flex-col-reverse gap-2.5 px-3 pt-4 overflow-y-scroll"> <div className="h-full flex flex-col-reverse gap-2.5 px-3 pt-4 overflow-y-scroll">
<div ref={intersectionRef} />
{messages && {messages &&
Object.entries(messages) Object.entries(messages)
.sort((a, b) => b[1].timestamp - a[1].timestamp) .sort((a, b) => b[1].timestamp - a[1].timestamp)
.map(([_, message]) => ( .map(([_, message]) => (
<TextMessage key={message.id} appRef={appRef} message={message} /> <TextMessage key={message.id} appRef={appRef} message={message} />
))} ))}
<div ref={intersectionRef} />
</div> </div>
<div className="pb-6 px-4 flex flex-row gap-3"> <div className="pb-6 px-4 flex flex-row gap-3">
@@ -107,15 +108,32 @@ export function TextMessage({
const [author, setAuthor] = useState<ClientMeta | null>(null); const [author, setAuthor] = useState<ClientMeta | null>(null);
const [verified, setVerified] = useState(true);
useEffect(() => { useEffect(() => {
const serverPubKey =
appRef.current?.server?.serverPublicKey &&
base58.encode(appRef.current?.server?.serverPublicKey);
if (!serverPubKey) return;
const authorPubKey = base58.decode(message.author);
setVerified(
ed.verify(
base58.decode(message.signature),
new TextEncoder().encode(
`${message.timestamp}@${serverPubKey}@${message.content}`,
),
authorPubKey,
),
);
const currentAccount = appRef.current?.getAccount(); const currentAccount = appRef.current?.getAccount();
if (currentAccount) { if (currentAccount) {
const pubKey = base58.encode( const pubKey = ed.getPublicKey(base58.decode(currentAccount.privateKey));
ed.getPublicKey(base58.decode(currentAccount.privateKey)),
);
if (pubKey === message.author) { if (pubKey === authorPubKey) {
setAuthor(currentAccount.meta); setAuthor(currentAccount.meta);
} }
} }
@@ -134,7 +152,10 @@ export function TextMessage({
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-sm font-medium"> <span className="text-sm font-medium flex items-center gap-1">
{!verified && (
<CircleXIcon className="h-3.5 w-3.5 text-destructive" />
)}
{author?.displayName || message.author.slice(0, 8)} {author?.displayName || message.author.slice(0, 8)}
</span> </span>
<span className="text-xs text-muted-foreground">{time}</span> <span className="text-xs text-muted-foreground">{time}</span>