Switch to X25519 + ML-KEM-768 encryption

This commit is contained in:
olcxja 2026-05-27 20:40:24 +02:00
commit a660ba32bd
9 changed files with 180 additions and 76 deletions

View file

@ -99,34 +99,33 @@ async function encryptWithNonce(value, key, nonce) {
return await encryptString(value, nonce + key);
}
async function calcCommunicationKeyClient(p, g, pubServer) {
const secretClientBytes = window.crypto.getRandomValues(new Uint8Array(512));
const secretClient = BigInt('0x' + Array.from(secretClientBytes).map(b => b.toString(16).padStart(2, '0')).join(''));
const pubClient = power(BigInt(g), secretClient, BigInt(p));
const sharedSecret = power(BigInt(pubServer), secretClient, BigInt(p));
async function calcHybridSharedKeyClient(pubX25519ServerBase64, pubMlKemServerBase64) {
const pubX25519Server = base64ToUint8(pubX25519ServerBase64);
const pubMlKemServer = base64ToUint8(pubMlKemServerBase64);
let sharedSecretStr = sharedSecret.toString(16);
// X25519
const privX25519Client = window.x25519.utils.randomSecretKey();
const pubX25519Client = window.x25519.getPublicKey(privX25519Client);
const secretX25519 = window.x25519.getSharedSecret(privX25519Client, pubX25519Server);
if (sharedSecretStr.length % 2 !== 0) {
sharedSecretStr = '0' + sharedSecretStr;
}
// ML-KEM-768
const mlkem = new window.MlKem768();
const [ciphertextMlKem, secretMlKem] = await mlkem.encap(pubMlKemServer);
const sharedSecretBytes = new Uint8Array(sharedSecretStr.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
// Combine and Hash: SHA256(X25519_Secret || MLKEM_Secret)
const combined = new Uint8Array(secretX25519.length + secretMlKem.length);
combined.set(secretX25519);
combined.set(secretMlKem, secretX25519.length);
const hashBuffer = await window.crypto.subtle.digest('SHA-256', sharedSecretBytes);
const hashBuffer = await window.crypto.subtle.digest('SHA-256', combined);
const aesKey = new Uint8Array(hashBuffer);
return [pubClient.toString(), aesKey];
return [uint8ToBase64(pubX25519Client), uint8ToBase64(ciphertextMlKem), aesKey];
}
function keyDataFromServerJson(jsonFromServer) {
const data = JSON.parse(jsonFromServer);
const p = BigInt(data.p);
const g = BigInt(data.g);
const pubServer = BigInt(data.pubServer);
return [p, g, pubServer, data.idKey]
return [data.pubX25519, data.pubMlKem, data.idKey]
}
function base64ToUint8(base64) {
@ -680,6 +679,12 @@ async function mainJS() {
lang = localStorage.getItem('lang');
}
if (host) {
await updateProtocolAndUrl(host);
} else {
await updateProtocolAndUrl(window.location.hostname);
}
if (!id && username) {
let resolvedId = await fetchAsync(`${url}/nametoid?u=${username}`);
if (resolvedId && !resolvedId.startsWith("error:")) {