mirror of
https://github.com/619dev/Paperphone-plus.git
synced 2026-09-03 07:18:05 +08:00
群聊加密模式fix6
This commit is contained in:
@@ -178,10 +178,20 @@ export async function distributeSenderKey(
|
||||
recipientIkPub: string,
|
||||
recipientKemPub: string | null | undefined
|
||||
): Promise<{ encrypted_key: string; header: string }> {
|
||||
const result = await encryptHybrid(recipientIkPub, recipientKemPub, senderKey)
|
||||
return {
|
||||
encrypted_key: result.ciphertext,
|
||||
header: result.header,
|
||||
try {
|
||||
const result = await encryptHybrid(recipientIkPub, recipientKemPub, senderKey)
|
||||
return {
|
||||
encrypted_key: result.ciphertext,
|
||||
header: result.header,
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[groupCrypto] distributeSenderKey failed:', {
|
||||
recipientIkPubLen: recipientIkPub?.length,
|
||||
kemPubPresent: !!recipientKemPub,
|
||||
senderKeyLen: senderKey?.length,
|
||||
error: err,
|
||||
})
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,5 +211,17 @@ export async function receiveSenderKey(
|
||||
myPrivKey: string,
|
||||
myKemPriv: string | null | undefined
|
||||
): Promise<string> {
|
||||
return await decryptHybrid(header, myPrivKey, myKemPriv, encryptedKey)
|
||||
try {
|
||||
const result = await decryptHybrid(header, myPrivKey, myKemPriv, encryptedKey)
|
||||
return result
|
||||
} catch (err) {
|
||||
console.error('[groupCrypto] receiveSenderKey decryption failed:', {
|
||||
encryptedKeyLen: encryptedKey?.length,
|
||||
headerLen: header?.length,
|
||||
myPrivKeyLen: myPrivKey?.length,
|
||||
kemPrivPresent: !!myKemPriv,
|
||||
error: err,
|
||||
})
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,12 +125,22 @@ export function useSocket() {
|
||||
await fetchAndStoreSenderKeys(data.group_id)
|
||||
sk = getSenderKey(data.group_id, data.from)
|
||||
}
|
||||
if (!sk) {
|
||||
// Still no key — retry after a brief delay.
|
||||
// The sender distributes keys via HTTP POST before sending the WS message,
|
||||
// but there can be a race where the message arrives before the POST response
|
||||
// has been fully committed to the DB.
|
||||
console.log(`[useSocket] Retrying sender key fetch after 1s delay for ${data.from} in group ${data.group_id}...`)
|
||||
await new Promise(r => setTimeout(r, 1000))
|
||||
await fetchAndStoreSenderKeys(data.group_id)
|
||||
sk = getSenderKey(data.group_id, data.from)
|
||||
}
|
||||
if (sk) {
|
||||
const text = await decryptWithSenderKey(data.ciphertext, data.nonce, sk.senderKey)
|
||||
msgToAdd = { ...data, decrypted: text }
|
||||
} else {
|
||||
// Still don't have sender key — store as 🔒 but keep nonce in data for retry
|
||||
console.warn(`[useSocket] Still no sender key for ${data.from} in group ${data.group_id} after fetch. Message will show 🔒`)
|
||||
console.warn(`[useSocket] Still no sender key for ${data.from} in group ${data.group_id} after retries. Message will show 🔒`)
|
||||
msgToAdd = { ...data, decrypted: '🔒' }
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -218,11 +228,15 @@ export function useSocket() {
|
||||
if (pending && data.msg_id) {
|
||||
const chatId = pending.group_id || pending.to
|
||||
if (chatId) {
|
||||
useStore.getState().addMessage(chatId, {
|
||||
// Build the message to add. For encrypted group messages, pendingMsg
|
||||
// now carries encryption metadata (ciphertext, nonce, sender_key_version)
|
||||
// set during the encryption step in Chat.tsx.
|
||||
const msgToStore: any = {
|
||||
...pending,
|
||||
id: data.msg_id,
|
||||
ts: data.ts || Date.now(),
|
||||
})
|
||||
}
|
||||
useStore.getState().addMessage(chatId, msgToStore)
|
||||
}
|
||||
;(window as any).__pendingMsg = null
|
||||
}
|
||||
|
||||
@@ -573,13 +573,20 @@ export default function Chat() {
|
||||
const memberKeys = await get(`/api/users/${m.id}`)
|
||||
ikPub = memberKeys?.ik_pub
|
||||
kemPub = memberKeys?.kem_pub
|
||||
} catch {}
|
||||
} catch (fetchKeyErr) {
|
||||
console.warn(`[Chat] Failed to fetch public keys for member ${m.id}:`, fetchKeyErr)
|
||||
}
|
||||
}
|
||||
if (ikPub && keys) {
|
||||
try {
|
||||
const dist = await distributeSenderKey(newKey, ikPub, kemPub)
|
||||
// Pass null for kemPub — kem_pub on server is an Ed25519 signing key,
|
||||
// NOT a valid Kyber KEM key. Passing it causes unnecessary Kyber encap
|
||||
// failures. Using null goes directly to pure ECDH (version 1).
|
||||
const dist = await distributeSenderKey(newKey, ikPub, null)
|
||||
distributions.push({ to_id: m.id, encrypted_key: dist.encrypted_key, header: dist.header })
|
||||
} catch {}
|
||||
} catch (distMemberErr) {
|
||||
console.warn(`[Chat] distributeSenderKey failed for member ${m.id}:`, distMemberErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (distributions.length > 0) {
|
||||
@@ -602,6 +609,12 @@ export default function Chat() {
|
||||
}
|
||||
if (sk && !sent) {
|
||||
const encrypted = await encryptWithSenderKey(content, sk.senderKey)
|
||||
// Update pendingMsg with actual encryption metadata so the ack handler
|
||||
// stores the message with correct encrypted fields (matching server data).
|
||||
// This ensures consistency when messages are later loaded from server.
|
||||
pendingMsg.ciphertext = encrypted.ciphertext
|
||||
pendingMsg.nonce = encrypted.nonce
|
||||
pendingMsg.sender_key_version = sk.keyVersion
|
||||
sent = sendWs({ type: 'message', msg_type: msgType, group_id: id, ciphertext: encrypted.ciphertext, nonce: encrypted.nonce, sender_key_version: sk.keyVersion })
|
||||
}
|
||||
} catch (encErr) {
|
||||
|
||||
Reference in New Issue
Block a user