Dateien nach "frontend/src/tools" hochladen
This commit is contained in:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user