From 858de0f76489a24c0fa464e028b1a0a969e3cec0 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 11:50:10 +0200 Subject: [PATCH 01/12] remove body, add more tests, simplify errors --- README.md | 71 +----------------- src/email-crypto/core.ts | 60 +++++++-------- src/email-crypto/coreSubject.ts | 55 +++++++------- .../hybridEncryptedEmailAndSubject.ts | 30 ++++---- src/email-crypto/hybridEncyptedEmail.ts | 37 +++++---- src/email-crypto/pwdProtectedEmail.ts | 10 +-- .../pwdProtectedEmailAndSubject.ts | 12 ++- src/index.ts | 12 +-- src/types.ts | 8 +- tests/email-crypto/core.test.ts | 34 ++++----- tests/email-crypto/hybridEmail.test.ts | 14 ++-- tests/email-crypto/pwdProtectedEmail.test.ts | 4 +- .../pwdProtectedEmailCoreErrors.test.ts | 73 ++++++++++++++++++ .../pwdProtectedEmailNobleErrors.test.ts | 75 +++++++++++++++++++ 14 files changed, 284 insertions(+), 211 deletions(-) create mode 100644 tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts create mode 100644 tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts diff --git a/README.md b/README.md index 6727a8b..88825e1 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ const emailAndSubject: EmailBodyAndSubject = { const encryptedEmailAndSubject = await encryptEmailAndSubjectHybrid(emailAndSubject, bobWithPublicKeys); const decryptedEmailAndSubject = await decryptEmailAndSubjectHybrid(encryptedEmailAndSubject, bobPrivateKeys); -expect(encryptedEmailAndSubject.encEmailBody.encSubject).not.toBe(emailAndSubject.subject); +expect(encryptedEmailAndSubject.encEmail.encSubject).not.toBe(emailAndSubject.subject); expect(decryptedEmailAndSubject).toStrictEqual(emailAndSubject); @@ -152,73 +152,4 @@ const resultRec = await openRecoveryKeystore(recoveryCodes, recoveryKeystore); expect(resultEnc).toStrictEqual(resultRec); -// Email storage and search - -// Between sessions emails are stored encrypted in IndexedDB. The encryption key is derived from user's mnemonic -// During the session, all emails are decrypted and stored in the cache (up to 600 MB, if excides - we delete oldests emails) -// For search, we build a search index from cache, then use Flexsearch for the search. -// The search is doen separately for email content, subject, sender and recivers. - -// Open IndexedDB database -const userID = 'user ID'; -const db = await openDatabase(userID); -const mnemonic = genMnemonic(); - -// Derive key for encrypting emails before storing them in a local database -const key = await deriveDatabaseKey(mnemonic); - -// Derive key for encrypting email draft -const key = await deriveDatabaseKey(mnemonic); - -// Encrypt and store one or several emails -await encryptAndStoreEmail(email, key, db); -await encryptAndStoreManyEmail(emails, key, db); - -// Delete given email by its ID -await deleteEmail(emailID, db); - -// Delete oldests emails -const number = 5; -await deleteOldestEmails(number, db); - -// Get all emails with or without sorting -const allEmails = await getAndDecryptAllEmails(key, db); -const newestFirst = await getAllEmailsSortedNewestFirst(db, key); -const oldestFirst = await getAllEmailsSortedOldestFirst(db, key); - -// Get the number of stored emails -const count = await getEmailCount(db); - -// Close IndexedDB database -closeDatabase(db); - -// Delete IndexedDB database -await deleteDatabase(userID); - -// Create email cache -const esCache = await createCacheFromDB(key, db); - -// Add one or multiple emails to cache -const result = addEmailToCache(email, esCache); -expect(result.success).toBe(true); - -const result = addEmailsToCache(emails, esCache); -expect(result.success).toBe(true); - -// Get email from cache by its ID -const email = await getEmailFromCache(emailID, esCache); - -// Delete email from cache by its ID -await deleteEmailFromCache(emailID, esCache); - -// Create search index and search by query - const searchIndex = await buildSearchIndexFromCache(esCache); - const query = 'keywords to search'; - const options = { - fields: ['subject'], // in which fields to search, all by deafult (subject, body, from, to) - limit: 5, // result limit, 50 by default - boost: { subject: 3, body: 1, from: 2, to: 2 }, // custom waights for matches in different email parts - }; - const result: EmailSearchResult = await searchEmails(query, esCache, searchIndex); - ``` \ No newline at end of file diff --git a/src/email-crypto/core.ts b/src/email-crypto/core.ts index 4a960f4..2c862ae 100644 --- a/src/email-crypto/core.ts +++ b/src/email-crypto/core.ts @@ -15,54 +15,54 @@ import { } from './errors'; /** - * Symmetrically encrypts email body. + * Symmetrically encrypts email. * - * @param body - The email body to encrypt. + * @param email - The email to encrypt. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The resulting encrypted email body and symmetric key used for encryption + * @returns The resulting encrypted email and symmetric key used for encryption */ -export async function encryptEmailBody( - body: EmailBody, +export async function encryptEmail( + email: EmailBody, aux?: Uint8Array, ): Promise<{ - encEmailBody: EmailBodyEncrypted; + encEmail: EmailBodyEncrypted; encryptionKey: Uint8Array; }> { - try { - if (!body.text) { + if (!email.text) { throw new InvalidInputEmail(); } + try { + const encryptionKey = genSymmetricKey(); - const encEmailBody = await encryptEmailBodyWithKey(body, encryptionKey, aux); + const encEmail = await encryptEmailWithKey(email, encryptionKey, aux); - return { encEmailBody, encryptionKey }; + return { encEmail, encryptionKey }; } catch (error) { - if (error instanceof InvalidInputEmail) throw error; throw new EmailSymmetricEncryptionError(error instanceof Error ? error.message : String(error)); } } /** - * Symmetrically encrypts email body with the given key. + * Symmetrically encrypts email with the given key. * - * @param body - The email body to encrypt. + * @param email - The email to encrypt. * @param encryptionKey - The symmetric key to encrypt the email. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The resulting encrypted email body and symmetric key used for encryption + * @returns The resulting encrypted email and symmetric key used for encryption */ -export async function encryptEmailBodyWithKey( - body: EmailBody, +export async function encryptEmailWithKey( + email: EmailBody, encryptionKey: Uint8Array, aux?: Uint8Array, ): Promise { try { - const text = UTF8ToUint8(body.text); + const text = UTF8ToUint8(email.text); const encryptedText = await encryptSymmetrically(encryptionKey, text, aux); const encText = uint8ArrayToBase64(encryptedText); const enc: EmailBodyEncrypted = { encText }; - if (body.attachments) { - const promises = body.attachments.map((attachment) => { + if (email.attachments) { + const promises = email.attachments.map((attachment) => { const binaryAttachment = UTF8ToUint8(attachment); return encryptSymmetrically(encryptionKey, binaryAttachment, aux); }); @@ -76,32 +76,32 @@ export async function encryptEmailBodyWithKey( } /** - * Decrypts symmetrically encrypted email body. + * Decrypts symmetrically encrypted email. * - * @param encEmailBody - The email body to decrypt. + * @param encEmail - The email to decrypt. * @param encryptionKey - The symmetric key to decrypt the email. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The resulting decrypted email body + * @returns The resulting decrypted email */ -export async function decryptEmailBody( - encEmailBody: EmailBodyEncrypted, +export async function decryptEmail( + encEmail: EmailBodyEncrypted, encryptionKey: Uint8Array, aux?: Uint8Array, ): Promise { try { - const encText = base64ToUint8Array(encEmailBody.encText); + const encText = base64ToUint8Array(encEmail.encText); const textArray = await decryptSymmetrically(encryptionKey, encText, aux); const text = uint8ToUTF8(textArray); - const body: EmailBody = { text }; + const email: EmailBody = { text }; - if (encEmailBody.encAttachments) { - const encAttachments = encEmailBody.encAttachments?.map(base64ToUint8Array); + if (encEmail.encAttachments) { + const encAttachments = encEmail.encAttachments?.map(base64ToUint8Array); const promises = encAttachments?.map((encAtt) => decryptSymmetrically(encryptionKey, encAtt, aux)); const decryptedAttachments = await Promise.all(promises); - body.attachments = decryptedAttachments?.map((att) => uint8ToUTF8(att)); + email.attachments = decryptedAttachments?.map((att) => uint8ToUTF8(att)); } - return body; + return email; } catch (error) { throw new EmailSymmetricDecryptionError(error instanceof Error ? error.message : String(error)); } diff --git a/src/email-crypto/coreSubject.ts b/src/email-crypto/coreSubject.ts index ab129f4..717f94e 100644 --- a/src/email-crypto/coreSubject.ts +++ b/src/email-crypto/coreSubject.ts @@ -1,85 +1,82 @@ import { EmailBodyAndSubject, EmailBodyAndSubjectEncrypted } from '../types'; import { encryptSymmetrically, decryptSymmetrically, genSymmetricKey } from '../symmetric-crypto'; -import { encryptEmailBodyWithKey, decryptEmailBody } from './core'; +import { encryptEmailWithKey, decryptEmail } from './core'; import { UTF8ToUint8, base64ToUint8Array, uint8ArrayToBase64, uint8ToUTF8 } from '../utils'; import { InvalidInputEmail, EmailSymmetricDecryptionError, EmailSymmetricEncryptionError } from './errors'; /** - * Symmetrically encrypts email body and subject. + * Symmetrically encrypts email and subject. * - * @param body - The email body and subject to encrypt. + * @param email - The email and subject to encrypt. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The resulting encrypted email body and symmetric key used for encryption + * @returns The resulting encrypted email and symmetric key used for encryption */ -export async function encryptEmailBodyAndSubject( - body: EmailBodyAndSubject, +export async function encryptEmailAndSubject( + email: EmailBodyAndSubject, aux?: Uint8Array, ): Promise<{ - encEmailBody: EmailBodyAndSubjectEncrypted; + encEmail: EmailBodyAndSubjectEncrypted; encryptionKey: Uint8Array; }> { - try { - if (!body.text || !body.subject) { + if (!email.text || !email.subject) { throw new InvalidInputEmail(); } + try { const encryptionKey = genSymmetricKey(); - const encEmailBody = await encryptEmailBodyAndSubjectWithKey(body, encryptionKey, aux); + const encEmail = await encryptEmailAndSubjectWithKey(email, encryptionKey, aux); - return { encEmailBody, encryptionKey }; + return { encEmail, encryptionKey }; } catch (error) { - if (error instanceof InvalidInputEmail) throw error; throw new EmailSymmetricEncryptionError(error instanceof Error ? error.message : String(error)); } } /** - * Symmetrically encrypts email body and subject with the given key. + * Symmetrically encrypts email and subject with the given key. * - * @param body - The email body and subject to encrypt. + * @param email - The email and subject to encrypt. * @param encryptionKey - The symmetric key to encrypt the email. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The resulting encrypted email body and symmetric key used for encryption + * @returns The resulting encrypted email and symmetric key used for encryption */ -export async function encryptEmailBodyAndSubjectWithKey( - body: EmailBodyAndSubject, +export async function encryptEmailAndSubjectWithKey( + email: EmailBodyAndSubject, encryptionKey: Uint8Array, aux?: Uint8Array, ): Promise { try { - const enc = await encryptEmailBodyWithKey(body, encryptionKey, aux); - const subject = UTF8ToUint8(body.subject); + const enc = await encryptEmailWithKey(email, encryptionKey, aux); + const subject = UTF8ToUint8(email.subject); const subjectEnc = await encryptSymmetrically(encryptionKey, subject, aux); const encSubject = uint8ArrayToBase64(subjectEnc); return { ...enc, encSubject }; } catch (error) { - if (error instanceof InvalidInputEmail) throw error; throw new EmailSymmetricEncryptionError(error instanceof Error ? error.message : String(error)); } } /** - * Decrypts symmetrically encrypted email body and subject. + * Decrypts symmetrically encrypted email and email subject. * - * @param encEmailBody - The email body and subject to decrypt. + * @param encEmail - The encrypted email and subject to decrypt. * @param encryptionKey - The symmetric key to decrypt the email. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The resulting decrypted email body + * @returns The resulting decrypted email and subject */ -export async function decryptEmailBodyAndSubject( - encEmailBody: EmailBodyAndSubjectEncrypted, +export async function decryptEmailAndSubject( + encEmail: EmailBodyAndSubjectEncrypted, encryptionKey: Uint8Array, aux?: Uint8Array, ): Promise { try { - const encSubject = base64ToUint8Array(encEmailBody.encSubject); + const encSubject = base64ToUint8Array(encEmail.encSubject); const subjectArray = await decryptSymmetrically(encryptionKey, encSubject, aux); const subject = uint8ToUTF8(subjectArray); - const body = await decryptEmailBody(encEmailBody, encryptionKey, aux); + const email = await decryptEmail(encEmail, encryptionKey, aux); - return { ...body, subject }; + return { ...email, subject }; } catch (error) { - if (error instanceof InvalidInputEmail) throw error; throw new EmailSymmetricDecryptionError(error instanceof Error ? error.message : String(error)); } } diff --git a/src/email-crypto/hybridEncryptedEmailAndSubject.ts b/src/email-crypto/hybridEncryptedEmailAndSubject.ts index 9c1033c..1a279e3 100644 --- a/src/email-crypto/hybridEncryptedEmailAndSubject.ts +++ b/src/email-crypto/hybridEncryptedEmailAndSubject.ts @@ -1,6 +1,6 @@ import { RecipientWithPublicKey, EmailBodyAndSubject, HybridEncryptedEmailAndSubject } from '../types'; import { encryptKeysHybrid, decryptKeysHybrid } from './core'; -import { encryptEmailBodyAndSubject, decryptEmailBodyAndSubject } from './coreSubject'; +import { encryptEmailAndSubject, decryptEmailAndSubject } from './coreSubject'; import { FailedToDecryptEmail, FailedToEncryptEmail, @@ -12,22 +12,22 @@ import { } from './errors'; /** - * Encrypts the email body and its subject using hybrid encryption. + * Encrypts the email and its subject using hybrid encryption. * - * @param body - The email body and subject to encrypt. + * @param email - The email and subject to encrypt. * @param recipientPublicKeys - The public keys of the recipient. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The encrypted email body + * @returns The encrypted email and subject */ export async function encryptEmailAndSubjectHybrid( - body: EmailBodyAndSubject, + email: EmailBodyAndSubject, recipient: RecipientWithPublicKey, aux?: Uint8Array, ): Promise { try { - const { encryptionKey, encEmailBody } = await encryptEmailBodyAndSubject(body, aux); + const { encryptionKey, encEmail } = await encryptEmailAndSubject(email, aux); const encryptedKey = await encryptKeysHybrid(encryptionKey, recipient); - return { encEmailBody, encryptedKey }; + return { encEmail, encryptedKey }; } catch (error) { if (error instanceof InvalidInputEmail) throw error; if (error instanceof EmailSymmetricEncryptionError) throw error; @@ -37,15 +37,15 @@ export async function encryptEmailAndSubjectHybrid( } /** - * Encrypts the email body and its subject using hybrid encryption for multiple recipients. + * Encrypts the email and its subject using hybrid encryption for multiple recipients. * - * @param body - The email body and subject to encrypt for multiple recipients. + * @param email - The email and subject to encrypt for multiple recipients. * @param recipients - The recipients with corresponding public keys. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The set of encrypted email bodies + * @returns The set of encrypted emails and subjects */ export async function encryptEmailAndSubjectHybridForMultipleRecipients( - body: EmailBodyAndSubject, + email: EmailBodyAndSubject, recipients: RecipientWithPublicKey[], aux?: Uint8Array, ): Promise { @@ -53,13 +53,13 @@ export async function encryptEmailAndSubjectHybridForMultipleRecipients( if (!recipients || recipients.length === 0) { throw new InvalidInputEmail(); } - const { encryptionKey, encEmailBody } = await encryptEmailBodyAndSubject(body, aux); + const { encryptionKey, encEmail } = await encryptEmailAndSubject(email, aux); const encryptedEmails: HybridEncryptedEmailAndSubject[] = []; for (const recipient of recipients) { const encryptedKey = await encryptKeysHybrid(encryptionKey, recipient); encryptedEmails.push({ - encEmailBody: encEmailBody, + encEmail, encryptedKey, }); } @@ -78,7 +78,7 @@ export async function encryptEmailAndSubjectHybridForMultipleRecipients( * @param hybridEmail - The encrypted email and subject. * @param recipientPrivateHybridKeys - The private key of the recipient. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The decrypted email body + * @returns The decrypted email and subject */ export async function decryptEmailAndSubjectHybrid( hybridEmail: HybridEncryptedEmailAndSubject, @@ -87,7 +87,7 @@ export async function decryptEmailAndSubjectHybrid( ): Promise { try { const encryptionKey = await decryptKeysHybrid(hybridEmail.encryptedKey, recipientPrivateHybridKeys); - return await decryptEmailBodyAndSubject(hybridEmail.encEmailBody, encryptionKey, aux); + return await decryptEmailAndSubject(hybridEmail.encEmail, encryptionKey, aux); } catch (error) { if (error instanceof InvalidInputEmail) throw error; if (error instanceof EmailHybridDecryptionError) throw error; diff --git a/src/email-crypto/hybridEncyptedEmail.ts b/src/email-crypto/hybridEncyptedEmail.ts index 31440a4..b166ec7 100644 --- a/src/email-crypto/hybridEncyptedEmail.ts +++ b/src/email-crypto/hybridEncyptedEmail.ts @@ -1,5 +1,5 @@ import { HybridEncryptedEmail, EmailBody, RecipientWithPublicKey } from '../types'; -import { decryptEmailBody, encryptKeysHybrid, decryptKeysHybrid, encryptEmailBody } from './core'; +import { decryptEmail, encryptKeysHybrid, decryptKeysHybrid, encryptEmail } from './core'; import { FailedToDecryptEmail, FailedToEncryptEmail, @@ -10,22 +10,22 @@ import { EmailSymmetricEncryptionError, } from './errors'; /** - * Encrypts the email body using hybrid encryption. + * Encrypts the email using hybrid encryption. * - * @param body - The email body to encrypt. + * @param email - The email to encrypt. * @param recipientPublicKeys - The public keys of the recipient. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The encrypted email body + * @returns The encrypted email */ export async function encryptEmailHybrid( - body: EmailBody, + email: EmailBody, recipient: RecipientWithPublicKey, aux?: Uint8Array, ): Promise { try { - const { encryptionKey, encEmailBody } = await encryptEmailBody(body, aux); + const { encryptionKey, encEmail } = await encryptEmail(email, aux); const encryptedKey = await encryptKeysHybrid(encryptionKey, recipient); - return { encEmailBody, encryptedKey }; + return { encEmail, encryptedKey }; } catch (error) { if (error instanceof InvalidInputEmail) throw error; if (error instanceof EmailSymmetricEncryptionError) throw error; @@ -35,15 +35,15 @@ export async function encryptEmailHybrid( } /** - * Encrypts the email body using hybrid encryption for multiple recipients. + * Encrypts the email using hybrid encryption for multiple recipients. * - * @param body - The email body to encrypt for multiple recipients. + * @param email - The email to encrypt for multiple recipients. * @param recipients - The recipients with corresponding public keys. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The set of encrypted email bodies + * @returns The set of encrypted emails */ export async function encryptEmailHybridForMultipleRecipients( - body: EmailBody, + email: EmailBody, recipients: RecipientWithPublicKey[], aux?: Uint8Array, ): Promise { @@ -51,13 +51,13 @@ export async function encryptEmailHybridForMultipleRecipients( if (!recipients || recipients.length === 0) { throw new InvalidInputEmail(); } - const { encryptionKey, encEmailBody } = await encryptEmailBody(body, aux); + const { encryptionKey, encEmail } = await encryptEmail(email, aux); const encryptedEmails: HybridEncryptedEmail[] = []; for (const recipient of recipients) { const encryptedKey = await encryptKeysHybrid(encryptionKey, recipient); encryptedEmails.push({ - encEmailBody: encEmailBody, + encEmail, encryptedKey, }); } @@ -73,20 +73,19 @@ export async function encryptEmailHybridForMultipleRecipients( /** * Decrypts the email using hybrid encryption. * - * @param encEmailBody - The encrypted email. + * @param encEmail - The encrypted email. * @param recipientPrivateHybridKeys - The private key of the recipient. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The decrypted email body + * @returns The decrypted email */ export async function decryptEmailHybrid( - encEmailBody: HybridEncryptedEmail, + encEmail: HybridEncryptedEmail, recipientPrivateHybridKeys: Uint8Array, aux?: Uint8Array, ): Promise { try { - const encryptionKey = await decryptKeysHybrid(encEmailBody.encryptedKey, recipientPrivateHybridKeys); - const body = await decryptEmailBody(encEmailBody.encEmailBody, encryptionKey, aux); - return body; + const encryptionKey = await decryptKeysHybrid(encEmail.encryptedKey, recipientPrivateHybridKeys); + return await decryptEmail(encEmail.encEmail, encryptionKey, aux); } catch (error) { if (error instanceof EmailHybridDecryptionError) throw error; if (error instanceof EmailSymmetricDecryptionError) throw error; diff --git a/src/email-crypto/pwdProtectedEmail.ts b/src/email-crypto/pwdProtectedEmail.ts index c9ed5cc..fba80e9 100644 --- a/src/email-crypto/pwdProtectedEmail.ts +++ b/src/email-crypto/pwdProtectedEmail.ts @@ -1,5 +1,5 @@ import { PwdProtectedEmail, EmailBody } from '../types'; -import { decryptEmailBody, passwordProtectKey, removePasswordProtection, encryptEmailBody } from './core'; +import { decryptEmail, passwordProtectKey, removePasswordProtection, encryptEmail } from './core'; import { EmailSymmetricEncryptionError, FailedToDecryptEmail, @@ -18,15 +18,15 @@ import { * @returns The password-protected email */ export async function createPwdProtectedEmail( - emailBody: EmailBody, + email: EmailBody, password: string, aux?: Uint8Array, ): Promise { try { - const { encryptionKey, encEmailBody } = await encryptEmailBody(emailBody, aux); + const { encryptionKey, encEmail } = await encryptEmail(email, aux); const encryptedKey = await passwordProtectKey(encryptionKey, password); - return { encEmailBody, encryptedKey }; + return { encEmail, encryptedKey }; } catch (error) { if (error instanceof InvalidInputEmail) throw error; if (error instanceof EmailSymmetricEncryptionError) throw error; @@ -50,7 +50,7 @@ export async function decryptPwdProtectedEmail( ): Promise { try { const encryptionKey = await removePasswordProtection(encryptedEmail.encryptedKey, password); - return await decryptEmailBody(encryptedEmail.encEmailBody, encryptionKey, aux); + return await decryptEmail(encryptedEmail.encEmail, encryptionKey, aux); } catch (error) { if (error instanceof InvalidInputEmail) throw error; if (error instanceof EmailPasswordOpenError) throw error; diff --git a/src/email-crypto/pwdProtectedEmailAndSubject.ts b/src/email-crypto/pwdProtectedEmailAndSubject.ts index 77bc069..b80ecc0 100644 --- a/src/email-crypto/pwdProtectedEmailAndSubject.ts +++ b/src/email-crypto/pwdProtectedEmailAndSubject.ts @@ -1,6 +1,6 @@ import { EmailBodyAndSubject, PwdProtectedEmailAndSubject } from '../types'; import { passwordProtectKey, removePasswordProtection } from './core'; -import { encryptEmailBodyAndSubject, decryptEmailBodyAndSubject } from './coreSubject'; +import { encryptEmailAndSubject, decryptEmailAndSubject } from './coreSubject'; import { FailedToDecryptEmail, FailedToEncryptEmail, @@ -20,15 +20,15 @@ import { * @returns The password-protected email */ export async function createPwdProtectedEmailAndSubject( - emailBody: EmailBodyAndSubject, + email: EmailBodyAndSubject, password: string, aux?: Uint8Array, ): Promise { try { - const { encryptionKey, encEmailBody } = await encryptEmailBodyAndSubject(emailBody, aux); + const { encryptionKey, encEmail } = await encryptEmailAndSubject(email, aux); const encryptedKey = await passwordProtectKey(encryptionKey, password); - return { encEmailBody, encryptedKey }; + return { encEmail, encryptedKey }; } catch (error) { if (error instanceof InvalidInputEmail) throw error; if (error instanceof EmailSymmetricEncryptionError) throw error; @@ -52,9 +52,7 @@ export async function decryptPwdProtectedEmailAndSubject( ): Promise { try { const encryptionKey = await removePasswordProtection(encryptedEmail.encryptedKey, password); - - const body = await decryptEmailBodyAndSubject(encryptedEmail.encEmailBody, encryptionKey, aux); - return body; + return await decryptEmailAndSubject(encryptedEmail.encEmail, encryptionKey, aux); } catch (error) { if (error instanceof InvalidInputEmail) throw error; if (error instanceof EmailPasswordOpenError) throw error; diff --git a/src/index.ts b/src/index.ts index 6e5b921..c3ec703 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,12 +13,12 @@ export { createPwdProtectedEmailAndSubject, decryptPwdProtectedEmailAndSubject, generateEmailKeys, - decryptEmailBody, - encryptEmailBody, - encryptEmailBodyWithKey, - encryptEmailBodyAndSubject, - encryptEmailBodyAndSubjectWithKey, - decryptEmailBodyAndSubject, + decryptEmail, + encryptEmail, + encryptEmailWithKey, + encryptEmailAndSubject, + encryptEmailAndSubjectWithKey, + decryptEmailAndSubject, deriveDatabaseKey, deriveEmailDraftKey, FailedToEncryptEmail, diff --git a/src/types.ts b/src/types.ts index d509628..4e3b786 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,22 +17,22 @@ export type HybridKeyPair = { export type HybridEncryptedEmail = { encryptedKey: HybridEncKey; - encEmailBody: EmailBodyEncrypted; + encEmail: EmailBodyEncrypted; }; export type HybridEncryptedEmailAndSubject = { encryptedKey: HybridEncKey; - encEmailBody: EmailBodyAndSubjectEncrypted; + encEmail: EmailBodyAndSubjectEncrypted; }; export type PwdProtectedEmail = { encryptedKey: PwdProtectedKey; - encEmailBody: EmailBodyEncrypted; + encEmail: EmailBodyEncrypted; }; export type PwdProtectedEmailAndSubject = { encryptedKey: PwdProtectedKey; - encEmailBody: EmailBodyAndSubjectEncrypted; + encEmail: EmailBodyAndSubjectEncrypted; }; export type HybridEncKey = { diff --git a/tests/email-crypto/core.test.ts b/tests/email-crypto/core.test.ts index a99d0ad..1d6f0e0 100644 --- a/tests/email-crypto/core.test.ts +++ b/tests/email-crypto/core.test.ts @@ -1,12 +1,12 @@ import { describe, expect, it } from 'vitest'; import { EmailBody, EmailBodyAndSubject } from '../../src/types'; import { - decryptEmailBody, - encryptEmailBody, + decryptEmail, + encryptEmail, deriveDatabaseKey, deriveEmailDraftKey, - encryptEmailBodyAndSubject, - decryptEmailBodyAndSubject, + encryptEmailAndSubject, + decryptEmailAndSubject, } from '../../src/email-crypto'; import { genMnemonic } from '../../src/utils'; import { genSymmetricKey } from '../../src/symmetric-crypto'; @@ -15,7 +15,7 @@ import { EmailSymmetricDecryptionError, InvalidInputEmail } from '../../src/emai describe('Test email crypto functions', () => { const emailBody: EmailBody = { - text: 'test body', + text: 'test email', }; const emailBodyAndSubject: EmailBodyAndSubject = { @@ -26,35 +26,35 @@ describe('Test email crypto functions', () => { const aux = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); it('should encrypt and decrypt email', async () => { - const { encEmailBody, encryptionKey } = await encryptEmailBody(emailBody, aux); - const result = await decryptEmailBody(encEmailBody, encryptionKey, aux); + const { encEmail, encryptionKey } = await encryptEmail(emailBody, aux); + const result = await decryptEmail(encEmail, encryptionKey, aux); expect(result).toEqual(emailBody); }); it('should encrypt and decrypt email with subject', async () => { - const { encEmailBody, encryptionKey } = await encryptEmailBodyAndSubject(emailBodyAndSubject, aux); - const result = await decryptEmailBodyAndSubject(encEmailBody, encryptionKey, aux); + const { encEmail, encryptionKey } = await encryptEmailAndSubject(emailBodyAndSubject, aux); + const result = await decryptEmailAndSubject(encEmail, encryptionKey, aux); expect(result).toEqual(emailBodyAndSubject); }); it('should throw an error if decryption fails', async () => { - const { encEmailBody, encryptionKey } = await encryptEmailBody(emailBody, aux); + const { encEmail, encryptionKey } = await encryptEmail(emailBody, aux); const badEncryptionKey = await genSymmetricKey(); - await expect(decryptEmailBody(encEmailBody, badEncryptionKey, aux)).rejects.toThrow(EmailSymmetricDecryptionError); + await expect(decryptEmail(encEmail, badEncryptionKey, aux)).rejects.toThrow(EmailSymmetricDecryptionError); const badAux = new Uint8Array([4, 5, 6, 7, 8]); - await expect(decryptEmailBody(encEmailBody, encryptionKey, badAux)).rejects.toThrow(EmailSymmetricDecryptionError); + await expect(decryptEmail(encEmail, encryptionKey, badAux)).rejects.toThrow(EmailSymmetricDecryptionError); }); it('should throw an error if decryption fails', async () => { - const { encEmailBody, encryptionKey } = await encryptEmailBodyAndSubject(emailBodyAndSubject, aux); + const { encEmail, encryptionKey } = await encryptEmailAndSubject(emailBodyAndSubject, aux); const badEncryptionKey = await genSymmetricKey(); - await expect(decryptEmailBodyAndSubject(encEmailBody, badEncryptionKey, aux)).rejects.toThrow( + await expect(decryptEmailAndSubject(encEmail, badEncryptionKey, aux)).rejects.toThrow( EmailSymmetricDecryptionError, ); const badAux = new Uint8Array([4, 5, 6, 7, 8]); - await expect(decryptEmailBodyAndSubject(encEmailBody, encryptionKey, badAux)).rejects.toThrow( + await expect(decryptEmailAndSubject(encEmail, encryptionKey, badAux)).rejects.toThrow( EmailSymmetricDecryptionError, ); }); @@ -63,8 +63,8 @@ describe('Test email crypto functions', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const badEmail: any = {}; badEmail.self = badEmail; - await expect(encryptEmailBody(badEmail, aux)).rejects.toThrow(InvalidInputEmail); - await expect(encryptEmailBodyAndSubject(badEmail, aux)).rejects.toThrow(InvalidInputEmail); + await expect(encryptEmail(badEmail, aux)).rejects.toThrow(InvalidInputEmail); + await expect(encryptEmailAndSubject(badEmail, aux)).rejects.toThrow(InvalidInputEmail); }); it('should derive symmetric key for database encryption', async () => { diff --git a/tests/email-crypto/hybridEmail.test.ts b/tests/email-crypto/hybridEmail.test.ts index 8e9d3f6..f9d0e09 100644 --- a/tests/email-crypto/hybridEmail.test.ts +++ b/tests/email-crypto/hybridEmail.test.ts @@ -56,7 +56,7 @@ describe('Test email crypto functions', async () => { it('should encrypt and decrypt email and subject sucessfully', async () => { const encryptedEmail = await encryptEmailAndSubjectHybrid(emailAndSubject, bobWithPublicKeys); - expect(encryptedEmail.encEmailBody?.encSubject).not.toBe(emailAndSubject.subject); + expect(encryptedEmail.encEmail?.encSubject).not.toBe(emailAndSubject.subject); const decryptedEmail = await decryptEmailAndSubjectHybrid(encryptedEmail, bobPrivateKeys); expect(decryptedEmail).toStrictEqual(emailAndSubject); @@ -88,14 +88,14 @@ describe('Test email crypto functions', async () => { }; const badEncryptedEmail: HybridEncryptedEmail = { encryptedKey: encKey, - encEmailBody: { + encEmail: { encText: 'mock encrypted text', }, }; const badEncryptedEmailAndSubject: HybridEncryptedEmailAndSubject = { encryptedKey: encKey, - encEmailBody: { + encEmail: { encText: 'mock encrypted text', encSubject: 'mock encrypted subject', }, @@ -115,7 +115,7 @@ describe('Test email crypto functions', async () => { ]); expect(encryptedEmail.length).toBe(2); - expect(encryptedEmail[0].encEmailBody).toBe(encryptedEmail[1].encEmailBody); + expect(encryptedEmail[0].encEmail).toBe(encryptedEmail[1].encEmail); }); it('should encrypt email and subject to multiple senders sucessfully', async () => { @@ -125,7 +125,7 @@ describe('Test email crypto functions', async () => { ]); expect(encryptedEmail.length).toBe(2); - expect(encryptedEmail[0].encEmailBody).toBe(encryptedEmail[1].encEmailBody); + expect(encryptedEmail[0].encEmail).toBe(encryptedEmail[1].encEmail); const emailDecryptedByBob = await decryptEmailAndSubjectHybrid(encryptedEmail[0], bobPrivateKeys); const emailDecryptedByAlice = await decryptEmailAndSubjectHybrid(encryptedEmail[1], alicePrivateKeys); @@ -200,7 +200,7 @@ describe('Test email crypto functions', async () => { const encryptedEmail = await encryptEmailHybrid(emailAndSubject, bobWithPublicKeys); const modifiedCiphertext = encryptedEmail; - modifiedCiphertext.encEmailBody.encText += 'modified ciphertext'; + modifiedCiphertext.encEmail.encText += 'modified ciphertext'; await expect(decryptEmailHybrid(modifiedCiphertext, bobPrivateKeys)).rejects.toThrow(EmailSymmetricDecryptionError); const modifiedKey = encryptedEmail; @@ -212,7 +212,7 @@ describe('Test email crypto functions', async () => { const encryptedEmail = await encryptEmailAndSubjectHybrid(emailAndSubject, bobWithPublicKeys); const modifiedCiphertext = encryptedEmail; - modifiedCiphertext.encEmailBody.encText += 'modified ciphertext'; + modifiedCiphertext.encEmail.encText += 'modified ciphertext'; await expect(decryptEmailAndSubjectHybrid(modifiedCiphertext, bobPrivateKeys)).rejects.toThrow( EmailSymmetricDecryptionError, ); diff --git a/tests/email-crypto/pwdProtectedEmail.test.ts b/tests/email-crypto/pwdProtectedEmail.test.ts index 7c38251..6406f92 100644 --- a/tests/email-crypto/pwdProtectedEmail.test.ts +++ b/tests/email-crypto/pwdProtectedEmail.test.ts @@ -56,7 +56,7 @@ describe('Test email crypto functions', () => { const encryptedEmail = await createPwdProtectedEmail(email, sharedSecret); const modifiedCiphertext = encryptedEmail; - modifiedCiphertext.encEmailBody.encText += 'modified ciphertext'; + modifiedCiphertext.encEmail.encText += 'modified ciphertext'; await expect(decryptPwdProtectedEmail(modifiedCiphertext, sharedSecret)).rejects.toThrow( EmailSymmetricDecryptionError, ); @@ -66,7 +66,7 @@ describe('Test email crypto functions', () => { const encryptedEmail = await createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret); const modifiedCiphertext = encryptedEmail; - modifiedCiphertext.encEmailBody.encText += 'modified ciphertext'; + modifiedCiphertext.encEmail.encText += 'modified ciphertext'; await expect(decryptPwdProtectedEmailAndSubject(modifiedCiphertext, sharedSecret)).rejects.toThrow( EmailSymmetricDecryptionError, ); diff --git a/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts b/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts new file mode 100644 index 0000000..b19d638 --- /dev/null +++ b/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts @@ -0,0 +1,73 @@ +import { vi, it, describe, beforeEach, expect } from 'vitest'; +import { createPwdProtectedEmail, createPwdProtectedEmailAndSubject, decryptPwdProtectedEmail, decryptPwdProtectedEmailAndSubject } from '../../src/email-crypto'; +import { FailedToDecryptEmail, FailedToEncryptEmail } from '../../src/email-crypto/errors'; +import { EmailBody, EmailBodyAndSubject, PwdProtectedEmail, PwdProtectedEmailAndSubject } from '../../src/types'; +import * as core from '../../src/email-crypto/core'; + +vi.mock('../../src/email-crypto/core', async () => { + const actual = await vi.importActual( + '../../src/email-crypto/core' + ); + + return { + ...actual, + passwordProtectKey: vi.fn(), + removePasswordProtection: vi.fn(), + }; +}); + +describe('Test email crypto functions', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + const email: EmailBody = { + text: 'Hi Bob, This is a test message. -Alice.', + attachments: ['file1.txt', 'file2.txt'], + }; + + + const emailAndSubject: EmailBodyAndSubject = { + text: 'Hi Bob, This is a test message. -Alice.', + attachments: ['file1.txt', 'file2.txt'], + subject: 'test subject', + }; + + const sharedSecret = 'test shared secret'; + +it('throws FailedToEncryptEmail when encryption fails', async () => { + const spy = vi.spyOn(core, 'passwordProtectKey'); + + spy.mockRejectedValue( + new Error('passwordProtectKey: unexpected failure'), + ); + + await expect( + createPwdProtectedEmail(email, sharedSecret), + ).rejects.toBeInstanceOf(FailedToEncryptEmail); + + await expect( + createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret), + ).rejects.toBeInstanceOf(FailedToEncryptEmail); +}); + +it('throws FailedToDecryptEmail when decryption fails', async () => { + const encryptedEmail = {} as PwdProtectedEmail; + const encryptedEmailAndSubject = {} as PwdProtectedEmailAndSubject; + + const spy = vi.spyOn(core, 'removePasswordProtection'); + + spy.mockRejectedValue( + new Error('removePasswordProtection: unexpected failure'), + ); + + await expect( + decryptPwdProtectedEmail(encryptedEmail, sharedSecret), + ).rejects.toBeInstanceOf(FailedToDecryptEmail); + + await expect( + decryptPwdProtectedEmailAndSubject(encryptedEmailAndSubject, sharedSecret), + ).rejects.toBeInstanceOf(FailedToDecryptEmail); +}); + +}); \ No newline at end of file diff --git a/tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts b/tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts new file mode 100644 index 0000000..725f02b --- /dev/null +++ b/tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts @@ -0,0 +1,75 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { + createPwdProtectedEmail, + createPwdProtectedEmailAndSubject, + EmailPasswordProtectError, + EmailSymmetricEncryptionError, +} from '../../src/email-crypto'; +import { EmailBody, EmailBodyAndSubject } from '../../src/types'; +import * as nobleUtils from '@noble/hashes/utils.js'; +import * as nobleWrapper from '@noble/ciphers/aes.js'; + +// Noble is ESM module and doesn't work with spyOn directly (module namespace is not configurable in ESM), must be mocked before. +// To mock it but keep the original implementation for most tests, we use importActual. +// vi.resetAllMocks(); before each test is a must to reset the mock back to importActual. +vi.mock('@noble/hashes/utils.js', async () => { + const actual = await vi.importActual< + typeof import('@noble/hashes/utils.js') + >('@noble/hashes/utils.js'); + + return { + ...actual, + randomBytes: vi.fn(actual.randomBytes), + }; +}); + + +vi.mock('@noble/ciphers/aes.js', async () => { + const actual = await vi.importActual< + typeof import('@noble/ciphers/aes.js') + >('@noble/ciphers/aes.js'); + + return { + ...actual, + aeskw: vi.fn(actual.aeskw), + }; +}); + + +describe('Test email crypto functions', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + const email: EmailBody = { + text: 'Hi Bob, This is a test message. -Alice.', + }; + + const emailAndSubject: EmailBodyAndSubject = { + text: 'Hi Bob, This is a test message. -Alice.', + subject: 'test subject', + }; + + const sharedSecret = 'test shared secret'; + + it('throws EmailSymmetricEncryptionError when symmetric encryption fails', async () => { + + vi.spyOn(nobleUtils, 'randomBytes').mockImplementation(() => { + throw new Error('randomBytes: unexpected crypto failure'); + }); + + await expect(createPwdProtectedEmail(email, sharedSecret)).rejects.toThrow(EmailSymmetricEncryptionError); + await expect(createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret)).rejects.toThrow(EmailSymmetricEncryptionError); + }); + + it('throws EmailPasswordProtectError when key wrapping fails', async () => { + + vi.spyOn(nobleWrapper, 'aeskw').mockImplementation(() => { + throw new Error('aeskw: unexpected crypto failure'); + }); + + await expect(createPwdProtectedEmail(email, sharedSecret)).rejects.toThrow(EmailPasswordProtectError); + await expect(createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret)).rejects.toThrow(EmailPasswordProtectError); + }); + +}); From cc70210fcf091eab7642610ee1ebe6abda409693 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 11:50:49 +0200 Subject: [PATCH 02/12] add missing style changes --- src/email-crypto/core.ts | 5 +- src/email-crypto/coreSubject.ts | 4 +- src/email-crypto/hybridEncyptedEmail.ts | 2 +- .../pwdProtectedEmailCoreErrors.test.ts | 59 ++++++++----------- .../pwdProtectedEmailNobleErrors.test.ts | 27 ++++----- 5 files changed, 42 insertions(+), 55 deletions(-) diff --git a/src/email-crypto/core.ts b/src/email-crypto/core.ts index 2c862ae..6f49a04 100644 --- a/src/email-crypto/core.ts +++ b/src/email-crypto/core.ts @@ -29,10 +29,9 @@ export async function encryptEmail( encryptionKey: Uint8Array; }> { if (!email.text) { - throw new InvalidInputEmail(); - } + throw new InvalidInputEmail(); + } try { - const encryptionKey = genSymmetricKey(); const encEmail = await encryptEmailWithKey(email, encryptionKey, aux); diff --git a/src/email-crypto/coreSubject.ts b/src/email-crypto/coreSubject.ts index 717f94e..c8d42b2 100644 --- a/src/email-crypto/coreSubject.ts +++ b/src/email-crypto/coreSubject.ts @@ -19,8 +19,8 @@ export async function encryptEmailAndSubject( encryptionKey: Uint8Array; }> { if (!email.text || !email.subject) { - throw new InvalidInputEmail(); - } + throw new InvalidInputEmail(); + } try { const encryptionKey = genSymmetricKey(); const encEmail = await encryptEmailAndSubjectWithKey(email, encryptionKey, aux); diff --git a/src/email-crypto/hybridEncyptedEmail.ts b/src/email-crypto/hybridEncyptedEmail.ts index b166ec7..f9f2968 100644 --- a/src/email-crypto/hybridEncyptedEmail.ts +++ b/src/email-crypto/hybridEncyptedEmail.ts @@ -15,7 +15,7 @@ import { * @param email - The email to encrypt. * @param recipientPublicKeys - The public keys of the recipient. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The encrypted email + * @returns The encrypted email */ export async function encryptEmailHybrid( email: EmailBody, diff --git a/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts b/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts index b19d638..a4889ab 100644 --- a/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts +++ b/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts @@ -1,13 +1,16 @@ import { vi, it, describe, beforeEach, expect } from 'vitest'; -import { createPwdProtectedEmail, createPwdProtectedEmailAndSubject, decryptPwdProtectedEmail, decryptPwdProtectedEmailAndSubject } from '../../src/email-crypto'; +import { + createPwdProtectedEmail, + createPwdProtectedEmailAndSubject, + decryptPwdProtectedEmail, + decryptPwdProtectedEmailAndSubject, +} from '../../src/email-crypto'; import { FailedToDecryptEmail, FailedToEncryptEmail } from '../../src/email-crypto/errors'; import { EmailBody, EmailBodyAndSubject, PwdProtectedEmail, PwdProtectedEmailAndSubject } from '../../src/types'; import * as core from '../../src/email-crypto/core'; vi.mock('../../src/email-crypto/core', async () => { - const actual = await vi.importActual( - '../../src/email-crypto/core' - ); + const actual = await vi.importActual('../../src/email-crypto/core'); return { ...actual, @@ -26,7 +29,6 @@ describe('Test email crypto functions', () => { attachments: ['file1.txt', 'file2.txt'], }; - const emailAndSubject: EmailBodyAndSubject = { text: 'Hi Bob, This is a test message. -Alice.', attachments: ['file1.txt', 'file2.txt'], @@ -35,39 +37,30 @@ describe('Test email crypto functions', () => { const sharedSecret = 'test shared secret'; -it('throws FailedToEncryptEmail when encryption fails', async () => { - const spy = vi.spyOn(core, 'passwordProtectKey'); + it('throws FailedToEncryptEmail when encryption fails', async () => { + const spy = vi.spyOn(core, 'passwordProtectKey'); - spy.mockRejectedValue( - new Error('passwordProtectKey: unexpected failure'), - ); + spy.mockRejectedValue(new Error('passwordProtectKey: unexpected failure')); - await expect( - createPwdProtectedEmail(email, sharedSecret), - ).rejects.toBeInstanceOf(FailedToEncryptEmail); + await expect(createPwdProtectedEmail(email, sharedSecret)).rejects.toBeInstanceOf(FailedToEncryptEmail); - await expect( - createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret), - ).rejects.toBeInstanceOf(FailedToEncryptEmail); -}); + await expect(createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret)).rejects.toBeInstanceOf( + FailedToEncryptEmail, + ); + }); -it('throws FailedToDecryptEmail when decryption fails', async () => { - const encryptedEmail = {} as PwdProtectedEmail; - const encryptedEmailAndSubject = {} as PwdProtectedEmailAndSubject; + it('throws FailedToDecryptEmail when decryption fails', async () => { + const encryptedEmail = {} as PwdProtectedEmail; + const encryptedEmailAndSubject = {} as PwdProtectedEmailAndSubject; - const spy = vi.spyOn(core, 'removePasswordProtection'); + const spy = vi.spyOn(core, 'removePasswordProtection'); - spy.mockRejectedValue( - new Error('removePasswordProtection: unexpected failure'), - ); - - await expect( - decryptPwdProtectedEmail(encryptedEmail, sharedSecret), - ).rejects.toBeInstanceOf(FailedToDecryptEmail); + spy.mockRejectedValue(new Error('removePasswordProtection: unexpected failure')); - await expect( - decryptPwdProtectedEmailAndSubject(encryptedEmailAndSubject, sharedSecret), - ).rejects.toBeInstanceOf(FailedToDecryptEmail); -}); + await expect(decryptPwdProtectedEmail(encryptedEmail, sharedSecret)).rejects.toBeInstanceOf(FailedToDecryptEmail); -}); \ No newline at end of file + await expect(decryptPwdProtectedEmailAndSubject(encryptedEmailAndSubject, sharedSecret)).rejects.toBeInstanceOf( + FailedToDecryptEmail, + ); + }); +}); diff --git a/tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts b/tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts index 725f02b..bd318e0 100644 --- a/tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts +++ b/tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts @@ -9,13 +9,11 @@ import { EmailBody, EmailBodyAndSubject } from '../../src/types'; import * as nobleUtils from '@noble/hashes/utils.js'; import * as nobleWrapper from '@noble/ciphers/aes.js'; -// Noble is ESM module and doesn't work with spyOn directly (module namespace is not configurable in ESM), must be mocked before. -// To mock it but keep the original implementation for most tests, we use importActual. +// Noble is ESM module and doesn't work with spyOn directly (module namespace is not configurable in ESM), must be mocked before. +// To mock it but keep the original implementation for most tests, we use importActual. // vi.resetAllMocks(); before each test is a must to reset the mock back to importActual. vi.mock('@noble/hashes/utils.js', async () => { - const actual = await vi.importActual< - typeof import('@noble/hashes/utils.js') - >('@noble/hashes/utils.js'); + const actual = await vi.importActual('@noble/hashes/utils.js'); return { ...actual, @@ -23,11 +21,8 @@ vi.mock('@noble/hashes/utils.js', async () => { }; }); - vi.mock('@noble/ciphers/aes.js', async () => { - const actual = await vi.importActual< - typeof import('@noble/ciphers/aes.js') - >('@noble/ciphers/aes.js'); + const actual = await vi.importActual('@noble/ciphers/aes.js'); return { ...actual, @@ -35,7 +30,6 @@ vi.mock('@noble/ciphers/aes.js', async () => { }; }); - describe('Test email crypto functions', () => { beforeEach(() => { vi.resetAllMocks(); @@ -52,24 +46,25 @@ describe('Test email crypto functions', () => { const sharedSecret = 'test shared secret'; - it('throws EmailSymmetricEncryptionError when symmetric encryption fails', async () => { - + it('throws EmailSymmetricEncryptionError when symmetric encryption fails', async () => { vi.spyOn(nobleUtils, 'randomBytes').mockImplementation(() => { throw new Error('randomBytes: unexpected crypto failure'); }); await expect(createPwdProtectedEmail(email, sharedSecret)).rejects.toThrow(EmailSymmetricEncryptionError); - await expect(createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret)).rejects.toThrow(EmailSymmetricEncryptionError); + await expect(createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret)).rejects.toThrow( + EmailSymmetricEncryptionError, + ); }); it('throws EmailPasswordProtectError when key wrapping fails', async () => { - vi.spyOn(nobleWrapper, 'aeskw').mockImplementation(() => { throw new Error('aeskw: unexpected crypto failure'); }); await expect(createPwdProtectedEmail(email, sharedSecret)).rejects.toThrow(EmailPasswordProtectError); - await expect(createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret)).rejects.toThrow(EmailPasswordProtectError); + await expect(createPwdProtectedEmailAndSubject(emailAndSubject, sharedSecret)).rejects.toThrow( + EmailPasswordProtectError, + ); }); - }); From b06b48c6e873f9184c8973628cc8804c4c8eea16 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 12:05:34 +0200 Subject: [PATCH 03/12] remove body from types --- README.md | 6 ++--- src/email-crypto/core.ts | 18 +++++++-------- src/email-crypto/coreSubject.ts | 14 ++++++------ .../hybridEncryptedEmailAndSubject.ts | 8 +++---- src/email-crypto/hybridEncyptedEmail.ts | 8 +++---- src/email-crypto/pwdProtectedEmail.ts | 8 +++---- .../pwdProtectedEmailAndSubject.ts | 8 +++---- src/types.ts | 16 +++++++------- tests/email-crypto/core.test.ts | 22 +++++++++---------- tests/email-crypto/hybridEmail.test.ts | 22 +++++++++---------- tests/email-crypto/pwdProtectedEmail.test.ts | 10 ++++----- .../pwdProtectedEmailCoreErrors.test.ts | 6 ++--- .../pwdProtectedEmailNobleErrors.test.ts | 6 ++--- 13 files changed, 76 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 88825e1..3a43ab0 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ const password = 'your password'; const { key, salt } = await getKeyFromPassword(password); // Hybrid email encryption -const email: EmailBody = { +const email: Email = { text: 'email text', attachments: ['email attachements'], }; @@ -119,10 +119,10 @@ const bobWithPublicKeys = { const encryptedEmail = await encryptEmailHybrid(email, bobWithPublicKeys); const decryptedEmail = await decryptEmailHybrid(encryptedEmail, bobPrivateKeys); -expect(decryptedEmailBody).toStrictEqual(email); +expect(decryptedEmail).toStrictEqual(email); // Hybrid email and subject encryption -const emailAndSubject: EmailBodyAndSubject = { +const emailAndSubject: EmailAndSubject = { text: 'email text', subject: 'email subject' attachments: ['email attachements'], diff --git a/src/email-crypto/core.ts b/src/email-crypto/core.ts index 6f49a04..9f4c138 100644 --- a/src/email-crypto/core.ts +++ b/src/email-crypto/core.ts @@ -1,4 +1,4 @@ -import { HybridEncKey, PwdProtectedKey, EmailBody, RecipientWithPublicKey, EmailBodyEncrypted } from '../types'; +import { HybridEncKey, PwdProtectedKey, Email, RecipientWithPublicKey, EmailEncrypted } from '../types'; import { encryptSymmetrically, decryptSymmetrically, genSymmetricKey } from '../symmetric-crypto'; import { encapsulateHybrid, decapsulateHybrid } from '../hybrid-crypto'; import { wrapKey, unwrapKey } from '../key-wrapper'; @@ -22,10 +22,10 @@ import { * @returns The resulting encrypted email and symmetric key used for encryption */ export async function encryptEmail( - email: EmailBody, + email: Email, aux?: Uint8Array, ): Promise<{ - encEmail: EmailBodyEncrypted; + encEmail: EmailEncrypted; encryptionKey: Uint8Array; }> { if (!email.text) { @@ -50,16 +50,16 @@ export async function encryptEmail( * @returns The resulting encrypted email and symmetric key used for encryption */ export async function encryptEmailWithKey( - email: EmailBody, + email: Email, encryptionKey: Uint8Array, aux?: Uint8Array, -): Promise { +): Promise { try { const text = UTF8ToUint8(email.text); const encryptedText = await encryptSymmetrically(encryptionKey, text, aux); const encText = uint8ArrayToBase64(encryptedText); - const enc: EmailBodyEncrypted = { encText }; + const enc: EmailEncrypted = { encText }; if (email.attachments) { const promises = email.attachments.map((attachment) => { const binaryAttachment = UTF8ToUint8(attachment); @@ -83,15 +83,15 @@ export async function encryptEmailWithKey( * @returns The resulting decrypted email */ export async function decryptEmail( - encEmail: EmailBodyEncrypted, + encEmail: EmailEncrypted, encryptionKey: Uint8Array, aux?: Uint8Array, -): Promise { +): Promise { try { const encText = base64ToUint8Array(encEmail.encText); const textArray = await decryptSymmetrically(encryptionKey, encText, aux); const text = uint8ToUTF8(textArray); - const email: EmailBody = { text }; + const email: Email = { text }; if (encEmail.encAttachments) { const encAttachments = encEmail.encAttachments?.map(base64ToUint8Array); diff --git a/src/email-crypto/coreSubject.ts b/src/email-crypto/coreSubject.ts index c8d42b2..f0002eb 100644 --- a/src/email-crypto/coreSubject.ts +++ b/src/email-crypto/coreSubject.ts @@ -1,4 +1,4 @@ -import { EmailBodyAndSubject, EmailBodyAndSubjectEncrypted } from '../types'; +import { EmailAndSubject, EmailAndSubjectEncrypted } from '../types'; import { encryptSymmetrically, decryptSymmetrically, genSymmetricKey } from '../symmetric-crypto'; import { encryptEmailWithKey, decryptEmail } from './core'; import { UTF8ToUint8, base64ToUint8Array, uint8ArrayToBase64, uint8ToUTF8 } from '../utils'; @@ -12,10 +12,10 @@ import { InvalidInputEmail, EmailSymmetricDecryptionError, EmailSymmetricEncrypt * @returns The resulting encrypted email and symmetric key used for encryption */ export async function encryptEmailAndSubject( - email: EmailBodyAndSubject, + email: EmailAndSubject, aux?: Uint8Array, ): Promise<{ - encEmail: EmailBodyAndSubjectEncrypted; + encEmail: EmailAndSubjectEncrypted; encryptionKey: Uint8Array; }> { if (!email.text || !email.subject) { @@ -40,10 +40,10 @@ export async function encryptEmailAndSubject( * @returns The resulting encrypted email and symmetric key used for encryption */ export async function encryptEmailAndSubjectWithKey( - email: EmailBodyAndSubject, + email: EmailAndSubject, encryptionKey: Uint8Array, aux?: Uint8Array, -): Promise { +): Promise { try { const enc = await encryptEmailWithKey(email, encryptionKey, aux); const subject = UTF8ToUint8(email.subject); @@ -65,10 +65,10 @@ export async function encryptEmailAndSubjectWithKey( * @returns The resulting decrypted email and subject */ export async function decryptEmailAndSubject( - encEmail: EmailBodyAndSubjectEncrypted, + encEmail: EmailAndSubjectEncrypted, encryptionKey: Uint8Array, aux?: Uint8Array, -): Promise { +): Promise { try { const encSubject = base64ToUint8Array(encEmail.encSubject); const subjectArray = await decryptSymmetrically(encryptionKey, encSubject, aux); diff --git a/src/email-crypto/hybridEncryptedEmailAndSubject.ts b/src/email-crypto/hybridEncryptedEmailAndSubject.ts index 1a279e3..180a011 100644 --- a/src/email-crypto/hybridEncryptedEmailAndSubject.ts +++ b/src/email-crypto/hybridEncryptedEmailAndSubject.ts @@ -1,4 +1,4 @@ -import { RecipientWithPublicKey, EmailBodyAndSubject, HybridEncryptedEmailAndSubject } from '../types'; +import { RecipientWithPublicKey, EmailAndSubject, HybridEncryptedEmailAndSubject } from '../types'; import { encryptKeysHybrid, decryptKeysHybrid } from './core'; import { encryptEmailAndSubject, decryptEmailAndSubject } from './coreSubject'; import { @@ -20,7 +20,7 @@ import { * @returns The encrypted email and subject */ export async function encryptEmailAndSubjectHybrid( - email: EmailBodyAndSubject, + email: EmailAndSubject, recipient: RecipientWithPublicKey, aux?: Uint8Array, ): Promise { @@ -45,7 +45,7 @@ export async function encryptEmailAndSubjectHybrid( * @returns The set of encrypted emails and subjects */ export async function encryptEmailAndSubjectHybridForMultipleRecipients( - email: EmailBodyAndSubject, + email: EmailAndSubject, recipients: RecipientWithPublicKey[], aux?: Uint8Array, ): Promise { @@ -84,7 +84,7 @@ export async function decryptEmailAndSubjectHybrid( hybridEmail: HybridEncryptedEmailAndSubject, recipientPrivateHybridKeys: Uint8Array, aux?: Uint8Array, -): Promise { +): Promise { try { const encryptionKey = await decryptKeysHybrid(hybridEmail.encryptedKey, recipientPrivateHybridKeys); return await decryptEmailAndSubject(hybridEmail.encEmail, encryptionKey, aux); diff --git a/src/email-crypto/hybridEncyptedEmail.ts b/src/email-crypto/hybridEncyptedEmail.ts index f9f2968..75be566 100644 --- a/src/email-crypto/hybridEncyptedEmail.ts +++ b/src/email-crypto/hybridEncyptedEmail.ts @@ -1,4 +1,4 @@ -import { HybridEncryptedEmail, EmailBody, RecipientWithPublicKey } from '../types'; +import { HybridEncryptedEmail, Email, RecipientWithPublicKey } from '../types'; import { decryptEmail, encryptKeysHybrid, decryptKeysHybrid, encryptEmail } from './core'; import { FailedToDecryptEmail, @@ -18,7 +18,7 @@ import { * @returns The encrypted email */ export async function encryptEmailHybrid( - email: EmailBody, + email: Email, recipient: RecipientWithPublicKey, aux?: Uint8Array, ): Promise { @@ -43,7 +43,7 @@ export async function encryptEmailHybrid( * @returns The set of encrypted emails */ export async function encryptEmailHybridForMultipleRecipients( - email: EmailBody, + email: Email, recipients: RecipientWithPublicKey[], aux?: Uint8Array, ): Promise { @@ -82,7 +82,7 @@ export async function decryptEmailHybrid( encEmail: HybridEncryptedEmail, recipientPrivateHybridKeys: Uint8Array, aux?: Uint8Array, -): Promise { +): Promise { try { const encryptionKey = await decryptKeysHybrid(encEmail.encryptedKey, recipientPrivateHybridKeys); return await decryptEmail(encEmail.encEmail, encryptionKey, aux); diff --git a/src/email-crypto/pwdProtectedEmail.ts b/src/email-crypto/pwdProtectedEmail.ts index fba80e9..ef2d3df 100644 --- a/src/email-crypto/pwdProtectedEmail.ts +++ b/src/email-crypto/pwdProtectedEmail.ts @@ -1,4 +1,4 @@ -import { PwdProtectedEmail, EmailBody } from '../types'; +import { PwdProtectedEmail, Email } from '../types'; import { decryptEmail, passwordProtectKey, removePasswordProtection, encryptEmail } from './core'; import { EmailSymmetricEncryptionError, @@ -18,7 +18,7 @@ import { * @returns The password-protected email */ export async function createPwdProtectedEmail( - email: EmailBody, + email: Email, password: string, aux?: Uint8Array, ): Promise { @@ -41,13 +41,13 @@ export async function createPwdProtectedEmail( * @param encryptedEmail - The encrypted email * @param password - The secret password shared among recipients. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The decrypted email body + * @returns The decrypted email */ export async function decryptPwdProtectedEmail( encryptedEmail: PwdProtectedEmail, password: string, aux?: Uint8Array, -): Promise { +): Promise { try { const encryptionKey = await removePasswordProtection(encryptedEmail.encryptedKey, password); return await decryptEmail(encryptedEmail.encEmail, encryptionKey, aux); diff --git a/src/email-crypto/pwdProtectedEmailAndSubject.ts b/src/email-crypto/pwdProtectedEmailAndSubject.ts index b80ecc0..2bdf596 100644 --- a/src/email-crypto/pwdProtectedEmailAndSubject.ts +++ b/src/email-crypto/pwdProtectedEmailAndSubject.ts @@ -1,4 +1,4 @@ -import { EmailBodyAndSubject, PwdProtectedEmailAndSubject } from '../types'; +import { EmailAndSubject, PwdProtectedEmailAndSubject } from '../types'; import { passwordProtectKey, removePasswordProtection } from './core'; import { encryptEmailAndSubject, decryptEmailAndSubject } from './coreSubject'; import { @@ -20,7 +20,7 @@ import { * @returns The password-protected email */ export async function createPwdProtectedEmailAndSubject( - email: EmailBodyAndSubject, + email: EmailAndSubject, password: string, aux?: Uint8Array, ): Promise { @@ -43,13 +43,13 @@ export async function createPwdProtectedEmailAndSubject( * @param encryptedEmail - The encrypted email and subject * @param password - The secret password shared among recipients. * @param aux - An optional auxilary sting for AEAD (e.g., email ID or timestamp). - * @returns The decrypted email body + * @returns The decrypted email and subject */ export async function decryptPwdProtectedEmailAndSubject( encryptedEmail: PwdProtectedEmailAndSubject, password: string, aux?: Uint8Array, -): Promise { +): Promise { try { const encryptionKey = await removePasswordProtection(encryptedEmail.encryptedKey, password); return await decryptEmailAndSubject(encryptedEmail.encEmail, encryptionKey, aux); diff --git a/src/types.ts b/src/types.ts index 4e3b786..47e7843 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,22 +17,22 @@ export type HybridKeyPair = { export type HybridEncryptedEmail = { encryptedKey: HybridEncKey; - encEmail: EmailBodyEncrypted; + encEmail: EmailEncrypted; }; export type HybridEncryptedEmailAndSubject = { encryptedKey: HybridEncKey; - encEmail: EmailBodyAndSubjectEncrypted; + encEmail: EmailAndSubjectEncrypted; }; export type PwdProtectedEmail = { encryptedKey: PwdProtectedKey; - encEmail: EmailBodyEncrypted; + encEmail: EmailEncrypted; }; export type PwdProtectedEmailAndSubject = { encryptedKey: PwdProtectedKey; - encEmail: EmailBodyAndSubjectEncrypted; + encEmail: EmailAndSubjectEncrypted; }; export type HybridEncKey = { @@ -46,21 +46,21 @@ export type PwdProtectedKey = { salt: string; }; -export type EmailBodyEncrypted = { +export type EmailEncrypted = { encText: string; encAttachments?: string[]; }; -export type EmailBodyAndSubjectEncrypted = EmailBodyEncrypted & { +export type EmailAndSubjectEncrypted = EmailEncrypted & { encSubject: string; }; -export type EmailBody = { +export type Email = { text: string; attachments?: string[]; }; -export type EmailBodyAndSubject = EmailBody & { +export type EmailAndSubject = Email & { subject: string; }; diff --git a/tests/email-crypto/core.test.ts b/tests/email-crypto/core.test.ts index 1d6f0e0..015b34e 100644 --- a/tests/email-crypto/core.test.ts +++ b/tests/email-crypto/core.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { EmailBody, EmailBodyAndSubject } from '../../src/types'; +import { Email, EmailAndSubject } from '../../src/types'; import { decryptEmail, encryptEmail, @@ -14,31 +14,31 @@ import { AES_KEY_BYTE_LENGTH } from '../../src/constants'; import { EmailSymmetricDecryptionError, InvalidInputEmail } from '../../src/email-crypto/errors'; describe('Test email crypto functions', () => { - const emailBody: EmailBody = { + const email: Email = { text: 'test email', }; - const emailBodyAndSubject: EmailBodyAndSubject = { - text: 'test body', - subject: 'test subject', + const emailAndSubject: EmailAndSubject = { + text: 'test email text', + subject: 'test email subject', }; const aux = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); it('should encrypt and decrypt email', async () => { - const { encEmail, encryptionKey } = await encryptEmail(emailBody, aux); + const { encEmail, encryptionKey } = await encryptEmail(email, aux); const result = await decryptEmail(encEmail, encryptionKey, aux); - expect(result).toEqual(emailBody); + expect(result).toEqual(email); }); it('should encrypt and decrypt email with subject', async () => { - const { encEmail, encryptionKey } = await encryptEmailAndSubject(emailBodyAndSubject, aux); + const { encEmail, encryptionKey } = await encryptEmailAndSubject(emailAndSubject, aux); const result = await decryptEmailAndSubject(encEmail, encryptionKey, aux); - expect(result).toEqual(emailBodyAndSubject); + expect(result).toEqual(emailAndSubject); }); it('should throw an error if decryption fails', async () => { - const { encEmail, encryptionKey } = await encryptEmail(emailBody, aux); + const { encEmail, encryptionKey } = await encryptEmail(email, aux); const badEncryptionKey = await genSymmetricKey(); await expect(decryptEmail(encEmail, badEncryptionKey, aux)).rejects.toThrow(EmailSymmetricDecryptionError); @@ -47,7 +47,7 @@ describe('Test email crypto functions', () => { }); it('should throw an error if decryption fails', async () => { - const { encEmail, encryptionKey } = await encryptEmailAndSubject(emailBodyAndSubject, aux); + const { encEmail, encryptionKey } = await encryptEmailAndSubject(emailAndSubject, aux); const badEncryptionKey = await genSymmetricKey(); await expect(decryptEmailAndSubject(encEmail, badEncryptionKey, aux)).rejects.toThrow( EmailSymmetricDecryptionError, diff --git a/tests/email-crypto/hybridEmail.test.ts b/tests/email-crypto/hybridEmail.test.ts index f9d0e09..d76ccbf 100644 --- a/tests/email-crypto/hybridEmail.test.ts +++ b/tests/email-crypto/hybridEmail.test.ts @@ -10,10 +10,10 @@ import { } from '../../src/email-crypto'; import { - EmailBody, + Email, HybridEncryptedEmail, HybridEncKey, - EmailBodyAndSubject, + EmailAndSubject, HybridEncryptedEmailAndSubject, RecipientWithPublicKey, } from '../../src/types'; @@ -25,13 +25,13 @@ import { } from '../../src/email-crypto/errors'; describe('Test email crypto functions', async () => { - const email: EmailBody = { - text: 'test body', + const email: Email = { + text: 'test email text', }; - const emailAndSubject: EmailBodyAndSubject = { - text: 'test body', - subject: 'test subject', + const emailAndSubject: EmailAndSubject = { + text: 'test email text', + subject: 'test email subject', }; const { secretKey: alicePrivateKeys, publicKey: alicePublicKeys } = await generateEmailKeys(); @@ -170,18 +170,18 @@ describe('Test email crypto functions', async () => { }); it('should throw an error if input is invalid', async () => { - await expect(encryptEmailHybrid({} as EmailBody, bobWithPublicKeys)).rejects.toThrow(InvalidInputEmail); + await expect(encryptEmailHybrid({} as Email, bobWithPublicKeys)).rejects.toThrow(InvalidInputEmail); - await expect(encryptEmailAndSubjectHybrid({} as EmailBodyAndSubject, bobWithPublicKeys)).rejects.toThrow( + await expect(encryptEmailAndSubjectHybrid({} as EmailAndSubject, bobWithPublicKeys)).rejects.toThrow( InvalidInputEmail, ); await expect( - encryptEmailHybridForMultipleRecipients({} as EmailBody, [bobWithPublicKeys, aliceWithPublicKeys]), + encryptEmailHybridForMultipleRecipients({} as Email, [bobWithPublicKeys, aliceWithPublicKeys]), ).rejects.toThrow(InvalidInputEmail); await expect( - encryptEmailAndSubjectHybridForMultipleRecipients({} as EmailBodyAndSubject, [ + encryptEmailAndSubjectHybridForMultipleRecipients({} as EmailAndSubject, [ bobWithPublicKeys, aliceWithPublicKeys, ]), diff --git a/tests/email-crypto/pwdProtectedEmail.test.ts b/tests/email-crypto/pwdProtectedEmail.test.ts index 6406f92..dadb3c4 100644 --- a/tests/email-crypto/pwdProtectedEmail.test.ts +++ b/tests/email-crypto/pwdProtectedEmail.test.ts @@ -8,14 +8,14 @@ import { EmailSymmetricDecryptionError, InvalidInputEmail, } from '../../src/email-crypto'; -import { EmailBody, EmailBodyAndSubject } from '../../src/types'; +import { Email, EmailAndSubject } from '../../src/types'; describe('Test email crypto functions', () => { - const email: EmailBody = { + const email: Email = { text: 'Hi Bob, This is a test message. -Alice.', }; - const emailAndSubject: EmailBodyAndSubject = { + const emailAndSubject: EmailAndSubject = { text: 'Hi Bob, This is a test message. -Alice.', subject: 'test subject', }; @@ -35,8 +35,8 @@ describe('Test email crypto functions', () => { }); it('should throw an error if encryption fails', async () => { - await expect(createPwdProtectedEmail({} as unknown as EmailBody, sharedSecret)).rejects.toThrow(InvalidInputEmail); - await expect(createPwdProtectedEmailAndSubject({} as unknown as EmailBodyAndSubject, sharedSecret)).rejects.toThrow( + await expect(createPwdProtectedEmail({} as unknown as Email, sharedSecret)).rejects.toThrow(InvalidInputEmail); + await expect(createPwdProtectedEmailAndSubject({} as unknown as EmailAndSubject, sharedSecret)).rejects.toThrow( InvalidInputEmail, ); }); diff --git a/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts b/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts index a4889ab..792eb7e 100644 --- a/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts +++ b/tests/email-crypto/pwdProtectedEmailCoreErrors.test.ts @@ -6,7 +6,7 @@ import { decryptPwdProtectedEmailAndSubject, } from '../../src/email-crypto'; import { FailedToDecryptEmail, FailedToEncryptEmail } from '../../src/email-crypto/errors'; -import { EmailBody, EmailBodyAndSubject, PwdProtectedEmail, PwdProtectedEmailAndSubject } from '../../src/types'; +import { Email, EmailAndSubject, PwdProtectedEmail, PwdProtectedEmailAndSubject } from '../../src/types'; import * as core from '../../src/email-crypto/core'; vi.mock('../../src/email-crypto/core', async () => { @@ -24,12 +24,12 @@ describe('Test email crypto functions', () => { vi.resetAllMocks(); }); - const email: EmailBody = { + const email: Email = { text: 'Hi Bob, This is a test message. -Alice.', attachments: ['file1.txt', 'file2.txt'], }; - const emailAndSubject: EmailBodyAndSubject = { + const emailAndSubject: EmailAndSubject = { text: 'Hi Bob, This is a test message. -Alice.', attachments: ['file1.txt', 'file2.txt'], subject: 'test subject', diff --git a/tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts b/tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts index bd318e0..b9d176f 100644 --- a/tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts +++ b/tests/email-crypto/pwdProtectedEmailNobleErrors.test.ts @@ -5,7 +5,7 @@ import { EmailPasswordProtectError, EmailSymmetricEncryptionError, } from '../../src/email-crypto'; -import { EmailBody, EmailBodyAndSubject } from '../../src/types'; +import { Email, EmailAndSubject } from '../../src/types'; import * as nobleUtils from '@noble/hashes/utils.js'; import * as nobleWrapper from '@noble/ciphers/aes.js'; @@ -35,11 +35,11 @@ describe('Test email crypto functions', () => { vi.resetAllMocks(); }); - const email: EmailBody = { + const email: Email = { text: 'Hi Bob, This is a test message. -Alice.', }; - const emailAndSubject: EmailBodyAndSubject = { + const emailAndSubject: EmailAndSubject = { text: 'Hi Bob, This is a test message. -Alice.', subject: 'test subject', }; From a68e007e8def322e654f046846c6194c997fdda3 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 12:41:42 +0200 Subject: [PATCH 04/12] remove github token --- .github/workflows/sonar-analysis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml index 2334c95..77236ce 100644 --- a/.github/workflows/sonar-analysis.yml +++ b/.github/workflows/sonar-analysis.yml @@ -38,5 +38,4 @@ jobs: - name: Analyze with SonarCloud uses: SonarSource/sonarqube-scan-action@v5.0.0 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret) From eb7e907982d8314c1ac8d47f93d5827c4e373045 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 14:32:48 +0200 Subject: [PATCH 05/12] remove playwright as it's in dependencies already --- .github/workflows/ci.yml | 3 --- .github/workflows/sonar-analysis.yml | 3 --- 2 files changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79a8954..3a1f840 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,9 +29,6 @@ jobs: - name: Install dependencies run: yarn - - name: Install Playwright Browsers - run: yarn playwright install - - name: Build run: yarn run build diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml index 77236ce..c5aedb3 100644 --- a/.github/workflows/sonar-analysis.yml +++ b/.github/workflows/sonar-analysis.yml @@ -31,9 +31,6 @@ jobs: - name: Install run: yarn install - - name: Check and Install Playwright - run: yarn playwright install - # Analyze with SonarCloud - name: Analyze with SonarCloud uses: SonarSource/sonarqube-scan-action@v5.0.0 From 56c5a43263c69d96dec74ad708ef4c2a5a989737 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 14:37:41 +0200 Subject: [PATCH 06/12] install only chromium --- .github/workflows/ci.yml | 3 +++ .github/workflows/sonar-analysis.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a1f840..01f0565 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,9 @@ jobs: - name: Install dependencies run: yarn + - name: Install Playwright Browsers + run: yarn playwright install --with-deps chromium + - name: Build run: yarn run build diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml index c5aedb3..a21bbcf 100644 --- a/.github/workflows/sonar-analysis.yml +++ b/.github/workflows/sonar-analysis.yml @@ -30,6 +30,9 @@ jobs: - name: Install run: yarn install + + - name: Check and Install Playwright + run: yarn playwright install --with-deps chromium # Analyze with SonarCloud - name: Analyze with SonarCloud From 519f36eee195a4b2ce18fc58975aef82dc54e9a2 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 14:43:35 +0200 Subject: [PATCH 07/12] remove with dep --- .github/workflows/ci.yml | 2 +- .github/workflows/sonar-analysis.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 01f0565..c338a48 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: run: yarn - name: Install Playwright Browsers - run: yarn playwright install --with-deps chromium + run: yarn playwright install chromium - name: Build run: yarn run build diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml index a21bbcf..a56be3e 100644 --- a/.github/workflows/sonar-analysis.yml +++ b/.github/workflows/sonar-analysis.yml @@ -30,9 +30,9 @@ jobs: - name: Install run: yarn install - + - name: Check and Install Playwright - run: yarn playwright install --with-deps chromium + run: yarn playwright install chromium # Analyze with SonarCloud - name: Analyze with SonarCloud From 36e8b7f3a6df48dc4d42811a6134706d9130c889 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 14:56:43 +0200 Subject: [PATCH 08/12] remove node version --- .github/workflows/sonar-analysis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml index a56be3e..4ed8c4e 100644 --- a/.github/workflows/sonar-analysis.yml +++ b/.github/workflows/sonar-analysis.yml @@ -25,7 +25,6 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node_version }} cache: yarn - name: Install From 6525784b4fdf4f068a2872c33a436e1311e7b665 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 15:01:06 +0200 Subject: [PATCH 09/12] remove yarn cache --- .github/workflows/ci.yml | 3 +-- .github/workflows/sonar-analysis.yml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c338a48..2ccab59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: '24.x' - cache: yarn - name: Add .npmrc run: cp .npmrc.template .npmrc @@ -30,7 +29,7 @@ jobs: run: yarn - name: Install Playwright Browsers - run: yarn playwright install chromium + run: yarn playwright install - name: Build run: yarn run build diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml index 4ed8c4e..827a67c 100644 --- a/.github/workflows/sonar-analysis.yml +++ b/.github/workflows/sonar-analysis.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - cache: yarn + node-version: ${{ matrix.node_version }} - name: Install run: yarn install From 063c51ec370da50cc6d0aa17003becd931fec0a2 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 15:04:38 +0200 Subject: [PATCH 10/12] back to 20 --- .github/workflows/ci.yml | 3 ++- .github/workflows/publish.yml | 2 +- .github/workflows/sonar-analysis.yml | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ccab59..067d1c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,8 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: '24.x' + node-version: '20.x' + cache: yarn - name: Add .npmrc run: cp .npmrc.template .npmrc diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0284f6e..1a86ec3 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: '24.x' + node-version: '20.x' cache: yarn - name: Install dependencies diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml index 827a67c..16a8dd3 100644 --- a/.github/workflows/sonar-analysis.yml +++ b/.github/workflows/sonar-analysis.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node_version: [24.x] + node_version: [20.x] steps: - name: Checkout @@ -26,6 +26,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node_version }} + cache: yarn - name: Install run: yarn install From ba19687423c5acdccd6c3659219dbeef9a5530ef Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 15:10:49 +0200 Subject: [PATCH 11/12] try 22 --- .github/workflows/ci.yml | 2 +- .github/workflows/publish.yml | 2 +- .github/workflows/sonar-analysis.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 067d1c5..1a17117 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: '20.x' + node-version: '22.x' cache: yarn - name: Add .npmrc diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1a86ec3..83f5d94 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: '20.x' + node-version: '22.x' cache: yarn - name: Install dependencies diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml index 16a8dd3..b9e842a 100644 --- a/.github/workflows/sonar-analysis.yml +++ b/.github/workflows/sonar-analysis.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node_version: [20.x] + node_version: [22.x] steps: - name: Checkout From 1b3aa037577ebee9cd0627b6c5641b00f462e404 Mon Sep 17 00:00:00 2001 From: tamarafinogina Date: Thu, 28 May 2026 15:18:51 +0200 Subject: [PATCH 12/12] remove shallow clones --- .github/workflows/sonar-analysis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml index b9e842a..6e0dc40 100644 --- a/.github/workflows/sonar-analysis.yml +++ b/.github/workflows/sonar-analysis.yml @@ -21,6 +21,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Setup Node uses: actions/setup-node@v4 @@ -32,7 +34,7 @@ jobs: run: yarn install - name: Check and Install Playwright - run: yarn playwright install chromium + run: yarn playwright install # Analyze with SonarCloud - name: Analyze with SonarCloud