Implement Signal Protocol

This commit is contained in:
2025-11-19 16:01:25 +03:00
Unverified
parent 8bda2220c6
commit d4b1e261d7
19 changed files with 1352 additions and 444 deletions
+15 -28
View File
@@ -1,7 +1,7 @@
import api from "@/core/api";
import type { CallSignalingMessage, WrappedSessionKeyPayload } from "@/core/types";
import type { CallSignalingMessage } from "@/core/types";
import { request } from "@/core/websocket";
import { wrapCallSessionKeyForRecipient, unwrapCallSessionKeyFromSender, rotateCallSessionKey } from "./encryption";
import { encryptCallSessionKey, decryptCallSessionKey, rotateCallSessionKey } from "./encryption";
import { importAesGcmKey } from "@/utils/crypto/symmetric";
import E2EEWorker from "./e2eeWorker?worker";
import { delay } from "@/utils/utils";
@@ -855,21 +855,18 @@ export async function sendCallSessionKey(userId: number, sessionKeyHash: string)
export async function sendWrappedCallSessionKey(userId: number, sessionKey: Uint8Array, sessionKeyHash: string): Promise<void> {
try {
const recipientPublicKey = await api.chats.dm.fetchUserPublicKey(userId, api.user.auth.getAuthToken()!);
if (!recipientPublicKey) {
console.warn("No recipient public key for", userId);
return;
}
const wrapped = await wrapCallSessionKeyForRecipient(recipientPublicKey, sessionKey);
// Encrypt session key using Signal Protocol
const encrypted = await encryptCallSessionKey(userId, sessionKey);
await sendSignalingMessage({
type: "call_session_key",
fromUserId: 0,
toUserId: userId,
sessionKeyHash,
data: { wrappedSessionKey: wrapped }
data: { encryptedSessionKey: encrypted }
});
} catch (e) {
console.error("Failed to send wrapped session key:", e);
console.error("Failed to send encrypted session key:", e);
}
}
@@ -886,31 +883,21 @@ export async function setSessionKey(userId: number, keyBytes: Uint8Array): Promi
export async function receiveWrappedSessionKey(
fromUserId: number,
wrappedPayload: WrappedSessionKeyPayload,
sessionKeyHash?: string
encryptedKey: { type: number; body: string }
): Promise<void> {
try {
const senderPublicKey = await api.chats.dm.fetchUserPublicKey(fromUserId, api.user.auth.getAuthToken()!);
if (!senderPublicKey) {
console.error("Failed to get sender public key");
return;
}
if (!wrappedPayload || !sessionKeyHash) {
console.error("Missing wrapped payload or session key hash");
if (!encryptedKey) {
console.error("Missing encrypted session key");
return;
}
// Unwrap the session key from the encrypted payload
const unwrappedSessionKey = await unwrapCallSessionKeyFromSender(senderPublicKey, {
salt: wrappedPayload.salt,
iv2: wrappedPayload.iv2,
wrapped: wrappedPayload.wrapped
});
// Decrypt the session key using Signal Protocol
const sessionKey = await decryptCallSessionKey(fromUserId, encryptedKey);
// Use the unwrapped session key directly (both sides should have the same key)
await setSessionKey(fromUserId, unwrappedSessionKey);
// Use the decrypted session key for media encryption
await setSessionKey(fromUserId, sessionKey);
} catch (e) {
console.error("Failed to unwrap session key:", e);
console.error("Failed to decrypt session key:", e);
}
}