(feat): finish chat crypto migration
Some checks failed
/ build-web (push) Failing after 4m36s
/ build-desktop (linux) (push) Failing after 4m47s
/ build-mobile (push) Failing after 6m34s
/ release (push) Has been skipped

(wip): prep for full ECDH repo migration
This commit is contained in:
Alois 2026-07-06 19:23:37 +02:00
commit 2cd326d0b1
10 changed files with 195 additions and 339 deletions

View file

@ -49,12 +49,6 @@ describe("createCryptoActions", () => {
secret: string,
ciphertext: string,
): Promise<string> => `${secret}|${ciphertext}`,
getSharedSecret: async (
ownPrivateKey: string,
ownPublicKey: string,
otherPublicKey: string,
): Promise<string> =>
`${ownPrivateKey}.${ownPublicKey}.${otherPublicKey}`,
};
const actions = createCryptoActions(() => api);
@ -67,6 +61,5 @@ describe("createCryptoActions", () => {
).toBe("s|c");
expect(await actions.encryptText("s", "p")).toBe("s:p");
expect(await actions.decryptText("s", "c")).toBe("s|c");
expect(await actions.getSharedSecret("a", "b", "c")).toBe("a.b.c");
});
});

View file

@ -1,5 +1,5 @@
import { createContext, useContext } from "react";
import { base64ToBytes, bytesToBase64, crypto } from "mtp";
import { base64ToBytes, crypto } from "mtp";
type CryptoContextType = {
decrypt: (
@ -12,11 +12,6 @@ type CryptoContextType = {
input: Uint8Array<ArrayBuffer>,
) => Promise<Uint8Array<ArrayBuffer>>;
encryptText: (secret: string, plaintext: string) => Promise<string>;
getSharedSecret: (
ownPrivateKey: string,
ownPublicKey: string,
otherPublicKey: string,
) => Promise<string>;
};
export const context = createContext<CryptoContextType | undefined>(undefined);
@ -39,8 +34,6 @@ export function createCryptoActions(
encrypt: (secret, input) => requireApi().encrypt(secret, input),
encryptText: (secret, plaintext) =>
requireApi().encryptText(secret, plaintext),
getSharedSecret: (ownPrivateKey, ownPublicKey, otherPublicKey) =>
requireApi().getSharedSecret(ownPrivateKey, ownPublicKey, otherPublicKey),
};
}
@ -58,45 +51,6 @@ function secretKeyFromString(secret: string): Uint8Array {
);
}
function compareBytes(a: Uint8Array, b: Uint8Array): number {
const len = Math.min(a.byteLength, b.byteLength);
for (let i = 0; i < len; i++) {
const diff = a[i] - b[i];
if (diff !== 0) return diff;
}
return a.byteLength - b.byteLength;
}
async function getSharedSecret(
ownPrivateKey: string,
ownPublicKey: string,
otherPublicKey: string,
): Promise<string> {
crypto.keyringToKeys(ownPrivateKey);
const ownKeys = crypto.publicKeyBundleToKeys(ownPublicKey);
const otherKeys = crypto.publicKeyBundleToKeys(otherPublicKey);
const publicKeys = [ownKeys.kemPublicKey, otherKeys.kemPublicKey].sort(
compareBytes,
);
const input = new Uint8Array(
publicKeys[0].byteLength + publicKeys[1].byteLength,
);
input.set(publicKeys[0]);
input.set(publicKeys[1], publicKeys[0].byteLength);
return bytesToBase64(
crypto.deriveEncryptionKey(
input,
new Uint8Array(0),
new TextEncoder().encode("tensamin:legacy-shared-secret"),
),
);
}
export default function Provider(props: { children: React.ReactNode }) {
const actions = createCryptoActions(() => ({
decrypt: async (secret, input) =>
@ -107,7 +61,6 @@ export default function Provider(props: { children: React.ReactNode }) {
ownedBytes(await crypto.encrypt(secretKeyFromString(secret), input)),
encryptText: (secret, plaintext) =>
crypto.encryptText(secretKeyFromString(secret), plaintext),
getSharedSecret,
}));
return <context.Provider value={actions}>{props.children}</context.Provider>;