(wip): call migration stuff

This commit is contained in:
Alois 2026-07-06 20:02:21 +02:00
commit 27f08e92e5
4 changed files with 264 additions and 53 deletions

View file

@ -5,7 +5,8 @@
"type": "module",
"exports": {
"./context": "./src/context.tsx",
"./chatSecret": "./src/chatSecret.ts"
"./chatSecret": "./src/chatSecret.ts",
"./callSecret": "./src/callSecret.ts"
},
"scripts": {
"format": "pnpm exec prettier --write .",

View file

@ -0,0 +1,112 @@
import { crypto } from "mtp";
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
export const CALL_SECRET_WRAPPING_SCHEME =
"mtp-call-secret-kem-chacha20poly1305-hkdf-sha256-v1";
const CALL_SECRET_SALT = textEncoder.encode("tensamin-call-secret-v1");
export function deriveCallSecretId(callId: string): string {
return `call:${callId}:main`;
}
export function ownKemPublicKeyFromKeyring(keyring: string): Uint8Array {
return crypto.keyringToKeys(keyring).kemPublicKey;
}
export function kemPublicKeyFromPublicKeyBundle(publicKey: string): Uint8Array {
return crypto.publicKeyBundleToKeys(publicKey).kemPublicKey;
}
export async function wrapCallSecret(args: {
callSecret: string;
recipientKemPublicKey: Uint8Array;
callId: string;
secretId: string;
version: number;
}): Promise<{
encryptedSecret: Uint8Array;
kemCiphertext: Uint8Array;
wrappingScheme: string;
}> {
const enc = crypto.encapsulate(args.recipientKemPublicKey);
try {
const wrappingKey = deriveWrappingKey({
sharedSecret: enc.shared_secret,
callId: args.callId,
secretId: args.secretId,
version: args.version,
});
try {
return {
encryptedSecret: await crypto.encrypt(
wrappingKey,
textEncoder.encode(args.callSecret),
),
kemCiphertext: enc.ciphertext,
wrappingScheme: CALL_SECRET_WRAPPING_SCHEME,
};
} finally {
wrappingKey.fill(0);
}
} finally {
enc.shared_secret.fill(0);
}
}
export async function unwrapCallSecret(args: {
encryptedSecret: Uint8Array;
kemCiphertext: Uint8Array;
keyring: string;
callId: string;
secretId: string;
version: number;
wrappingScheme: string;
}): Promise<string> {
if (args.wrappingScheme !== CALL_SECRET_WRAPPING_SCHEME) {
throw new Error(
`Unsupported call secret wrapping scheme: ${args.wrappingScheme}`,
);
}
const ownKeys = crypto.keyringToKeys(args.keyring);
const sharedSecret = crypto.decapsulate(
ownKeys.kemSecretKey,
args.kemCiphertext,
);
try {
const wrappingKey = deriveWrappingKey({
sharedSecret,
callId: args.callId,
secretId: args.secretId,
version: args.version,
});
try {
return textDecoder.decode(
await crypto.decrypt(wrappingKey, args.encryptedSecret),
);
} finally {
wrappingKey.fill(0);
}
} finally {
sharedSecret.fill(0);
}
}
function deriveWrappingKey(args: {
sharedSecret: Uint8Array;
callId: string;
secretId: string;
version: number;
}): Uint8Array {
return crypto.deriveEncryptionKey(
args.sharedSecret,
CALL_SECRET_SALT,
textEncoder.encode(`${args.callId}:${args.secretId}:${args.version}`),
);
}