feat: implement on-demand sharing key derivation

This commit is contained in:
ramvignesh-b
2026-04-24 06:33:23 +05:30
parent a84d837942
commit 00c16627cc
2 changed files with 61 additions and 0 deletions
+41
View File
@@ -190,3 +190,44 @@ describe("Sharing Key Decryption", () => {
expect(decryptedLetter).toBe(letterContent);
});
});
describe("extractSharingKey", () => {
let masterKey: CryptoKey;
beforeEach(async () => {
utils = new CryptoUtils();
await utils.initialize();
const bundle = await CryptoUtils.deriveKeyBundle(
"password",
"test@test.com",
);
masterKey = bundle.masterKey;
});
it("should return the same key that encryptLetter embedded as sharingKey", async () => {
const encrypted = await utils.encryptLetter("any content", masterKey);
const extracted = await utils.extractSharingKey(
encrypted.encrypted_dek,
masterKey,
);
expect(extracted).toBe(encrypted.sharingKey);
});
it("extracted key should decrypt the ciphertext produced by encryptLetter", async () => {
const plaintext = "hello from the owner";
const encrypted = await utils.encryptLetter(plaintext, masterKey);
const extracted = await utils.extractSharingKey(
encrypted.encrypted_dek,
masterKey,
);
const decrypted = await utils.decryptLetterWithSharingKey(
encrypted.encrypted_content,
extracted,
);
expect(decrypted).toBe(plaintext);
});
});
+20
View File
@@ -309,4 +309,24 @@ export class CryptoUtils {
);
return URL.createObjectURL(new Blob([bytes]));
}
// Re-derives the sharing key (raw DEK) on demand (browser only, not sent to server).
public async extractSharingKey(
encrypted_dek: string,
masterKey: CryptoKey,
): Promise<string> {
const [dekIv, wrappedDek] = this.unpackWithIv(encrypted_dek);
const rawDek = await crypto.subtle.unwrapKey(
"raw",
wrappedDek,
masterKey,
{ name: "AES-GCM", iv: dekIv },
CryptoUtils.AES_GCM,
true,
["decrypt"],
);
return this.toBase64(
new Uint8Array(await crypto.subtle.exportKey("raw", rawDek)),
);
}
}