Switch to X25519 + ML-KEM-768 encryption
This commit is contained in:
parent
9e6d128839
commit
a660ba32bd
9 changed files with 180 additions and 76 deletions
14
webroot/crypto-pq.js
Normal file
14
webroot/crypto-pq.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -6,6 +6,7 @@
|
|||
<title>Miarven</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
||||
<script src="crypto-pq.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<loading>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
<title>Miarven - Login</title>
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<link rel="icon" type="image/svg+xml" href="../favicon.svg">
|
||||
<script src="../crypto-pq.js"></script>
|
||||
<style>
|
||||
html {
|
||||
font-size: max(17px, calc(100vw / 100));
|
||||
|
|
@ -321,7 +322,7 @@
|
|||
formLogin.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
|
||||
await updateProtocolAndUrl(loginHost.value);
|
||||
let res = await Auth(loginUsername.value, loginPassword.value);
|
||||
|
||||
if (res.startsWith("success:"))
|
||||
|
|
@ -375,14 +376,18 @@
|
|||
container.className = 'auth-container show-register';
|
||||
return;
|
||||
}
|
||||
|
||||
await updateProtocolAndUrl(registerHost.value);
|
||||
|
||||
let dataarray = keyDataFromServerJson(await fetchAsync(`${url}/createaccount?step=init`));
|
||||
var sharedkey = await calcCommunicationKeyClient(dataarray[0], dataarray[1], dataarray[2]);
|
||||
sharedpvkey = sharedkey[1];
|
||||
createId = dataarray[3];
|
||||
var sharedkey = await calcHybridSharedKeyClient(dataarray[0], dataarray[1]);
|
||||
sharedpvkey = sharedkey[2];
|
||||
createId = dataarray[2];
|
||||
const captchaimage = await fetch(`${url}/createaccount?step=register`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
pubClient: sharedkey[0],
|
||||
pubX25519: sharedkey[0],
|
||||
ciphertextMlKem: sharedkey[1],
|
||||
idKey: createId,
|
||||
username: await encrypt(registerUsername.value, sharedpvkey),
|
||||
password: await encrypt(await hashSHA3_512(registerPassword.value), sharedpvkey)
|
||||
|
|
|
|||
|
|
@ -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:")) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue