feat: enhance zero-knowledge authentication by deriving and sending auth hashes to the server

This commit is contained in:
ramvignesh-b
2026-04-14 22:44:42 +05:30
parent 3e5dbbe3f3
commit 967b3a77f8
10 changed files with 146 additions and 79 deletions
+27 -7
View File
@@ -65,34 +65,54 @@ export class CryptoUtils {
};
/**
* Derives a Master Key from a password + email (salt).
* Same credentials = same key.
* Derives a Key Bundle (MasterKey + AuthHash) from a password + email.
* Absolute zero knowledge!!
*/
public static async deriveMasterKey(
public static async deriveKeyBundle(
password: string,
email: string,
): Promise<CryptoKey> {
): Promise<{ masterKey: CryptoKey; authHash: string }> {
const enc = new TextEncoder();
const salt = enc.encode(email.toLowerCase());
const baseKey = await crypto.subtle.importKey(
"raw",
enc.encode(password),
"PBKDF2",
false,
["deriveKey"],
["deriveBits", "deriveKey"],
);
return crypto.subtle.deriveKey(
const masterSeed = await crypto.subtle.deriveBits(
{
name: "PBKDF2",
salt: enc.encode(email.toLowerCase()),
salt,
iterations: CryptoUtils.PBKDF2_ITERATIONS,
hash: "SHA-256",
},
baseKey,
512, // 512 bits to split
);
// first 256 bits for MasterKey, last 256 bits for AuthHash
const masterKeyBytes = masterSeed.slice(0, 32);
const authHashBytes = masterSeed.slice(32, 64);
// Create the MasterKey for client-side encryption
const masterKey = await crypto.subtle.importKey(
"raw",
masterKeyBytes,
CryptoUtils.AES_GCM,
false,
["encrypt", "decrypt", "wrapKey", "unwrapKey"],
);
// Create the hex AuthHash for server-side verification
const authHash = Array.from(new Uint8Array(authHashBytes))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
return { masterKey, authHash };
}
// Internal helper to encrypt data and wrap the key