diff --git a/frontend/src/utils/crypto.test.ts b/frontend/src/utils/crypto.test.ts index 549d8bd..eff276c 100644 --- a/frontend/src/utils/crypto.test.ts +++ b/frontend/src/utils/crypto.test.ts @@ -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); + }); +}); diff --git a/frontend/src/utils/crypto.ts b/frontend/src/utils/crypto.ts index 18e760d..c540136 100644 --- a/frontend/src/utils/crypto.ts +++ b/frontend/src/utils/crypto.ts @@ -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 { + 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)), + ); + } }