Dateien nach "frontend/src/tools" hochladen

This commit is contained in:
2026-05-28 00:32:59 +02:00
parent 172e150e2b
commit 8ae4267edf

View File

@@ -4,6 +4,7 @@ import { UserIcon, TrashIcon } from '../icons.jsx';
import { import {
getOrCreateKeyPair, getOrCreateKeyPair,
getPublicKeyJwk, getPublicKeyJwk,
getFingerprint,
deriveSharedKey, deriveSharedKey,
encryptMsg, encryptMsg,
decryptMsg, decryptMsg,
@@ -17,6 +18,7 @@ export default function Nachrichten({ toast, mobile }) {
const [messages, setMessages] = useState([]); const [messages, setMessages] = useState([]);
const [sharedKey, setSharedKey] = useState(null); // null | CryptoKey | 'error' const [sharedKey, setSharedKey] = useState(null); // null | CryptoKey | 'error'
const [decrypted, setDecrypted] = useState({}); const [decrypted, setDecrypted] = useState({});
const [partnerFp, setPartnerFp] = useState('');
const [text, setText] = useState(''); const [text, setText] = useState('');
const [sending, setSending] = useState(false); const [sending, setSending] = useState(false);
const bottomRef = useRef(null); const bottomRef = useRef(null);
@@ -45,16 +47,22 @@ export default function Nachrichten({ toast, mobile }) {
return () => clearInterval(pollRef.current); return () => clearInterval(pollRef.current);
}, [activeUser?.id]); }, [activeUser?.id]);
// ── Shared Key ─────────────────────────────────────────────────────────────── // ── Shared Key + Partner-Fingerprint ─────────────────────────────────────────
useEffect(() => { useEffect(() => {
setSharedKey(null); setSharedKey(null);
setDecrypted({}); setDecrypted({});
setPartnerFp('');
if (!kp || !activeUser?.public_key) return; if (!kp || !activeUser?.public_key) return;
try { try {
const pubJwk = JSON.parse(activeUser.public_key); const pubJwk = JSON.parse(activeUser.public_key);
deriveSharedKey(kp.privateKey, pubJwk) deriveSharedKey(kp.privateKey, pubJwk)
.then(setSharedKey) .then(setSharedKey)
.catch(() => setSharedKey('error')); .catch(() => setSharedKey('error'));
// Fingerprint des Partner-Keys berechnen (aus dem auf dem Server gespeicherten Public Key)
crypto.subtle.importKey('jwk', pubJwk, { name:'X25519' }, true, [])
.then(k => getFingerprint({ publicKey: k }))
.then(setPartnerFp)
.catch(() => setPartnerFp('?'));
} catch { } catch {
setSharedKey('error'); setSharedKey('error');
} }
@@ -241,10 +249,21 @@ export default function Nachrichten({ toast, mobile }) {
whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}> whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>
{activeUser.username} {activeUser.username}
</div> </div>
<div style={{ color: sharedKey === 'error' ? '#ff6b9d' : 'rgba(78,205,196,0.5)', {sharedKey === 'error' ? (
fontSize:8, fontFamily:'monospace', letterSpacing:1.5, marginTop:1 }}> <div style={{ color:'#ff6b9d', fontSize:8, fontFamily:'monospace', letterSpacing:1.5, marginTop:1 }}>
{sharedKey === 'error' ? '⚠ SCHLÜSSEL INKOMPATIBEL' : '🔒 E2E VERSCHLÜSSELT · X25519 + AES-256-GCM'} SCHLÜSSEL INKOMPATIBEL
</div> </div>
) : partnerFp ? (
<div title={`Fingerprint von ${activeUser.username} vergleiche mit dessen Anzeige unter Einstellungen → Sicherheit`}
style={{ color:'rgba(78,205,196,0.55)', fontSize:8, fontFamily:'monospace', letterSpacing:1, marginTop:2,
cursor:'help', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>
🔒 {partnerFp}
</div>
) : (
<div style={{ color:'rgba(78,205,196,0.35)', fontSize:8, fontFamily:'monospace', letterSpacing:1.5, marginTop:1 }}>
🔒 E2E VERSCHLÜSSELT · X25519 + AES-256-GCM
</div>
)}
</div> </div>
<button onClick={deleteConversation} style={{ ...S.btn('#ff6b9d', true), fontSize:10, flexShrink:0 }}> <button onClick={deleteConversation} style={{ ...S.btn('#ff6b9d', true), fontSize:10, flexShrink:0 }}>
Verlauf löschen Verlauf löschen