Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/sonar-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ jobs:
- name: Check and Install Playwright
run: yarn playwright install

- name: Unit test run
run: yarn test:coverage

# Analyze with SonarCloud
- name: Analyze with SonarCloud
uses: SonarSource/sonarqube-scan-action@v5.0.0
Expand Down
4 changes: 3 additions & 1 deletion src/email-crypto/pwdProtectedEmail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ export async function decryptPwdProtectedEmail(
password: string,
aux?: Uint8Array,
): Promise<Email> {
if (!encryptedEmail?.encEmail || !encryptedEmail?.encryptedKey) {
throw new InvalidInputEmail();
}
try {
const encryptionKey = await removePasswordProtection(encryptedEmail.encryptedKey, password);
return await decryptEmail(encryptedEmail.encEmail, encryptionKey, aux);
} catch (error) {
if (error instanceof InvalidInputEmail) throw error;
if (error instanceof EmailPasswordOpenError) throw error;
if (error instanceof EmailSymmetricDecryptionError) throw error;
throw new FailedToDecryptEmail(error instanceof Error ? error.message : String(error));
Expand Down
4 changes: 3 additions & 1 deletion src/email-crypto/pwdProtectedEmailAndSubject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@ export async function decryptPwdProtectedEmailAndSubject(
password: string,
aux?: Uint8Array,
): Promise<EmailAndSubject> {
if (!encryptedEmail?.encEmail || !encryptedEmail?.encryptedKey) {
throw new InvalidInputEmail();
}
try {
const encryptionKey = await removePasswordProtection(encryptedEmail.encryptedKey, password);
return await decryptEmailAndSubject(encryptedEmail.encEmail, encryptionKey, aux);
} catch (error) {
if (error instanceof InvalidInputEmail) throw error;
if (error instanceof EmailPasswordOpenError) throw error;
if (error instanceof EmailSymmetricDecryptionError) throw error;
throw new FailedToDecryptEmail(error instanceof Error ? error.message : String(error));
Expand Down
10 changes: 8 additions & 2 deletions tests/email-crypto/pwdProtectedEmail.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
EmailSymmetricDecryptionError,
InvalidInputEmail,
} from '../../src/email-crypto';
import { Email, EmailAndSubject } from '../../src/types';
import { Email, EmailAndSubject, PwdProtectedEmail, PwdProtectedEmailAndSubject } from '../../src/types';

describe('Test email crypto functions', () => {
const email: Email = {
Expand Down Expand Up @@ -52,9 +52,15 @@ describe('Test email crypto functions', () => {
);
});

it('should throw an error if input is invalid', async () => {
await expect(decryptPwdProtectedEmail({} as PwdProtectedEmail, sharedSecret)).rejects.toThrow(InvalidInputEmail);
await expect(decryptPwdProtectedEmailAndSubject({} as PwdProtectedEmailAndSubject, sharedSecret)).rejects.toThrow(
InvalidInputEmail,
);
});

it('should throw an error if password-protected email is modified', async () => {
const encryptedEmail = await createPwdProtectedEmail(email, sharedSecret);

const modifiedCiphertext = encryptedEmail;
modifiedCiphertext.encEmail.encText += 'modified ciphertext';
await expect(decryptPwdProtectedEmail(modifiedCiphertext, sharedSecret)).rejects.toThrow(
Expand Down
47 changes: 44 additions & 3 deletions tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,27 @@ describe('Test email crypto functions', () => {
);
});

it('throws FailedToDecryptEmail when decryption fails', async () => {
const encryptedEmail = {} as PwdProtectedEmail;
const encryptedEmailAndSubject = {} as PwdProtectedEmailAndSubject;
const encryptedEmail = {
encEmail: {
encText: 'Np2hSuNJinD6Z3KIiWhnH1qWBpaCed6dU2Du0JiDRxPLCJhMQ4FmQmqr5PRz3iHMWa7xWRRmsyeMyd1k8cwX4YRmVg==',
},
encryptedKey: {
encryptedKey: 'PTSrN0aCrMoQIeSxQmmCHBhPFnJw8hx+Xu4nnbW81I+dvbyGAZmEvQ==',
salt: 'JUh7Xi0arXw/bhF4d+IEAw==',
},
} as PwdProtectedEmail;
const encryptedEmailAndSubject = {
encEmail: {
encSubject: '6LD/Cnv8/mCDSns53eZzPRyNa3d9gk+gdkiFhzIGouxlQMBeE6YkOA==',
encText: 'nH1NJoZnO1Trv7RQ4+3Z+/epqt422zjJIBPO6nzSFq2pDlLvgOUcrqMlCicn+fK74XP7gYeKfd6z1Qa517XyPsCvdQ==',
},
encryptedKey: {
encryptedKey: 'ctMRiLHe5a3AxVIC8QdFOS7VXdVMrZOZsYuC5MUQ7jbzxQio75NN2g==',
salt: 'qWsXcWQlc/uo46b0c+if0A==',
},
} as PwdProtectedEmailAndSubject;

it('throws FailedToDecryptEmail when decryption fails', async () => {
const spy = vi.spyOn(core, 'removePasswordProtection');

spy.mockRejectedValue(new Error('removePasswordProtection: unexpected failure'));
Expand All @@ -63,4 +80,28 @@ describe('Test email crypto functions', () => {
FailedToDecryptEmail,
);
});

it('throws FailedToDecryptEmail when decryption fails with string error', async () => {
const spy = vi.spyOn(core, 'removePasswordProtection');

spy.mockRejectedValue('removePasswordProtection: unexpected failure');

await expect(decryptPwdProtectedEmail(encryptedEmail, sharedSecret)).rejects.toBeInstanceOf(FailedToDecryptEmail);

await expect(decryptPwdProtectedEmailAndSubject(encryptedEmailAndSubject, sharedSecret)).rejects.toBeInstanceOf(
FailedToDecryptEmail,
);
});

it('throws FailedToEncryptEmail when encryption fails with string error', async () => {
const spy = vi.spyOn(core, 'passwordProtectKey');

spy.mockRejectedValue('passwordProtectKey: unexpected failure');

await expect(createPwdProtectedEmail(email, sharedSecret)).rejects.toBeInstanceOf(FailedToEncryptEmail);

await expect(createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret)).rejects.toBeInstanceOf(
FailedToEncryptEmail,
);
});
});
Loading