diff --git a/src/email-crypto/converters.ts b/src/email-crypto/converters.ts index 2d8b7cb..3286408 100644 --- a/src/email-crypto/converters.ts +++ b/src/email-crypto/converters.ts @@ -1,31 +1,7 @@ -import { uint8ArrayToBase64, base64ToUint8Array, UTF8ToUint8, uint8ToUTF8 } from '../utils'; -import { - EmailBody, - HybridEncKey, - PwdProtectedKey, - User, - EmailPublicParameters, - HybridEncryptedEmail, - PwdProtectedEmail, - Email, -} from '../types'; +import { UTF8ToUint8, uint8ToUTF8 } from '../utils'; +import { EmailBody, User, Email } from '../types'; import { concatBytes } from '@noble/hashes/utils.js'; -/** - * Converts a User type into a base64 string. - * - * @param user - The given user. - * @returns The base64 representation of the user. - */ -export function userToBase64(user: User): string { - try { - const json = JSON.stringify(user); - return btoa(json); - } catch (error) { - throw new Error('Failed to convert User to base64', { cause: error }); - } -} - export function userToBytes(user: User): Uint8Array { try { const json = JSON.stringify(user); @@ -44,22 +20,6 @@ export function recipientsToBytes(recipients: User[]): Uint8Array { } } -/** - * Converts a base64 string into a User type - * - * @param base64 - The base64 representation of the user. - * @returns The User type. - */ -export function base64ToUser(base64: string): User { - try { - const json = atob(base64); - const user: User = JSON.parse(json); - return user; - } catch (error) { - throw new Error('Failed to convert base64 to User', { cause: error }); - } -} - /** * Converts an EmailBody type into a Uint8Array array. * @@ -91,163 +51,6 @@ export function binaryToEmailBody(array: Uint8Array): EmailBody { } } -/** - * Converts a hybrid key of the type HybridEncKey into base64 string. - * - * @param encHybridKey - The HybridEncKey key. - * @returns The resulting base64 key encoding. - */ -export function encHybridKeyToBase64(encHybridKey: HybridEncKey): string { - try { - const json = JSON.stringify({ - kyberCiphertext: uint8ArrayToBase64(encHybridKey.kyberCiphertext), - encryptedKey: uint8ArrayToBase64(encHybridKey.encryptedKey), - }); - const base64 = btoa(json); - return base64; - } catch (error) { - throw new Error('Failed to convert hybrid key to base64', { cause: error }); - } -} - -/** - * Converts a base64 string into a hybrid key of the type HybridEncKey. - * - * @param base - The base64 encoding of the hybrid key. - * @returns The resulting HybridEncKey key. - */ -export function base64ToEncHybridKey(base64: string): HybridEncKey { - try { - const json = atob(base64); - const obj = JSON.parse(json); - return { - encryptedKey: base64ToUint8Array(obj.encryptedKey), - kyberCiphertext: base64ToUint8Array(obj.kyberCiphertext), - }; - } catch (error) { - throw new Error('Failed to convert base64 to hybrid key', { cause: error }); - } -} - -/** - * Converts a password-protected key of the type PwdProtectedKey into base64 string. - * - * @param pwdProtectedKey - The password-protected key of the type PwdProtectedKey. - * @returns The resulting base64 key encoding. - */ -export function pwdProtectedKeyToBase64(pwdProtectedKey: PwdProtectedKey): string { - try { - const json = JSON.stringify({ - encryptedKey: uint8ArrayToBase64(pwdProtectedKey.encryptedKey), - salt: uint8ArrayToBase64(pwdProtectedKey.salt), - }); - const base64 = btoa(json); - return base64; - } catch (error) { - throw new Error('Failed to convert password-protected key to base64', { cause: error }); - } -} - -/** - * Converts a base64 string into a password-protected key of the type PwdProtectedKey. - * - * @param base64 - The base64 string. - * @returns The resulting PwdProtectedKey key. - */ -export function base64ToPwdProtectedKey(base64: string): PwdProtectedKey { - try { - const json = atob(base64); - const obj = JSON.parse(json); - return { - encryptedKey: base64ToUint8Array(obj.encryptedKey), - salt: base64ToUint8Array(obj.salt), - }; - } catch (error) { - throw new Error('Failed to convert base64 to password-protected key', { cause: error }); - } -} - -/** - * Converts an email public parameters of type EmailPublicParameters into base64 string. - * - * @param params - The EmailPublicParameters email paramaters. - * @returns The resulting base64 string encoding. - */ -export function paramsToBase64(params: EmailPublicParameters): string { - try { - const json = JSON.stringify({ - ...params, - sender: userToBase64(params.sender), - recipient: userToBase64(params.recipient), - recipients: params.recipients?.map(userToBase64), - }); - const base64 = btoa(json); - return base64; - } catch (error) { - throw new Error('Failed to convert email public parameters to base64', { cause: error }); - } -} - -/** - * Converts a base64 string into an email paramaters of the type EmailPublicParameters. - * - * @param base64 - The base64 string. - * @returns The resulting EmailPublicParameters email parameters. - */ -export function base64ToParams(base64: string): EmailPublicParameters { - try { - const json = atob(base64); - const obj = JSON.parse(json); - return { - ...obj, - sender: base64ToUser(obj.sender), - recipient: base64ToUser(obj.recipient), - recipients: obj.recipients?.map(base64ToUser), - }; - } catch (error) { - throw new Error('Failed to convert base64 to email params', { cause: error }); - } -} - -/** - * Converts an encrypted via hybrid encryption email into base64 string. - * - * @param email - The HybridEncryptedEmail encrypted via hybrid encryption email. - * @returns The resulting base64 string encoding. - */ -export function hybridEncyptedEmailToBase64(email: HybridEncryptedEmail): string { - try { - const json = JSON.stringify({ - encryptedKey: encHybridKeyToBase64(email.encryptedKey), - enc: uint8ArrayToBase64(email.enc), - recipientEmail: email.recipientEmail, - }); - const base64 = btoa(json); - return base64; - } catch (error) { - throw new Error('Failed to convert hybrid email to base64', { cause: error }); - } -} - -/** - * Converts a pwd protected email into base64 string. - * - * @param email - The PwdProtectedEmail pwd protected email. - * @returns The resulting base64 string encoding. - */ -export function pwdProtectedEmailToBase64(email: PwdProtectedEmail): string { - try { - const json = JSON.stringify({ - encryptedKey: pwdProtectedKeyToBase64(email.encryptedKey), - enc: uint8ArrayToBase64(email.enc), - }); - const base64 = btoa(json); - return base64; - } catch (error) { - throw new Error('Failed to convert pwd protected email to base64', { cause: error }); - } -} - /** * Converts an Email type into a Uint8Array array. * diff --git a/src/email-crypto/core.ts b/src/email-crypto/core.ts index 470e157..c0d9b74 100644 --- a/src/email-crypto/core.ts +++ b/src/email-crypto/core.ts @@ -5,7 +5,7 @@ import { encapsulateKyber, decapsulateKyber } from '../post-quantum-crypto'; import { deriveWrappingKey, wrapKey, unwrapKey, importWrappingKey } from '../key-wrapper'; import { deriveSecretKey } from '../asymmetric-crypto'; import { getKeyFromPassword, getKeyFromPasswordAndSalt } from '../derive-key'; -import { UTF8ToUint8, uint8ToUTF8, uuidToBytes } from '../utils'; +import { UTF8ToUint8, base64ToUint8Array, uint8ArrayToBase64, uint8ToUTF8, uuidToBytes } from '../utils'; /** * Symmetrically encrypts an email with a randomly sampled key. @@ -141,7 +141,10 @@ export async function encryptKeysHybrid( ); const wrappingKey = await deriveWrappingKey(eccSecret, kyberSecret); const encryptedKey = await wrapKey(emailEncryptionKey, wrappingKey); - return { encryptedKey, kyberCiphertext }; + const encryptedKeyBase64 = uint8ArrayToBase64(encryptedKey); + const kyberCiphertextBase64 = uint8ArrayToBase64(kyberCiphertext); + + return { encryptedKey: encryptedKeyBase64, kyberCiphertext: kyberCiphertextBase64 }; } catch (error) { throw new Error('Failed to encrypt email key using hybrid encryption', { cause: error }); } @@ -161,10 +164,12 @@ export async function decryptKeysHybrid( recipientPrivateKey: PrivateKeys, ): Promise { try { + const kyberCiphertext = base64ToUint8Array(encryptedKey.kyberCiphertext); + const encKey = base64ToUint8Array(encryptedKey.encryptedKey); const eccSecret = await deriveSecretKey(senderPublicKey.eccPublicKey, recipientPrivateKey.eccPrivateKey); - const kyberSecret = decapsulateKyber(encryptedKey.kyberCiphertext, recipientPrivateKey.kyberPrivateKey); + const kyberSecret = decapsulateKyber(kyberCiphertext, recipientPrivateKey.kyberPrivateKey); const wrappingKey = await deriveWrappingKey(eccSecret, kyberSecret); - const encryptionKey = await unwrapKey(encryptedKey.encryptedKey, wrappingKey); + const encryptionKey = await unwrapKey(encKey, wrappingKey); return encryptionKey; } catch (error) { throw new Error('Failed to decrypt email key encrypted via hybrid encryption', { cause: error }); @@ -183,7 +188,9 @@ export async function passwordProtectKey(emailEncryptionKey: CryptoKey, password const { key, salt } = await getKeyFromPassword(password); const wrappingKey = await importWrappingKey(key); const encryptedKey = await wrapKey(emailEncryptionKey, wrappingKey); - return { encryptedKey, salt }; + const saltStr = uint8ArrayToBase64(salt); + const encryptedKeyStr = uint8ArrayToBase64(encryptedKey); + return { encryptedKey: encryptedKeyStr, salt: saltStr }; } catch (error) { throw new Error('Failed to password-protect email key', { cause: error }); } @@ -201,9 +208,11 @@ export async function removePasswordProtection( password: string, ): Promise { try { - const key = await getKeyFromPasswordAndSalt(password, emailEncryptionKey.salt); + const salt = base64ToUint8Array(emailEncryptionKey.salt); + const encryptedKey = base64ToUint8Array(emailEncryptionKey.encryptedKey); + const key = await getKeyFromPasswordAndSalt(password, salt); const wrappingKey = await importWrappingKey(key); - const encryptionKey = await unwrapKey(emailEncryptionKey.encryptedKey, wrappingKey); + const encryptionKey = await unwrapKey(encryptedKey, wrappingKey); return encryptionKey; } catch (error) { throw new Error('Failed to remove password-protection from email key', { cause: error }); diff --git a/src/email-crypto/hybridEncyptedEmail.ts b/src/email-crypto/hybridEncyptedEmail.ts index 23faa3f..565156d 100644 --- a/src/email-crypto/hybridEncyptedEmail.ts +++ b/src/email-crypto/hybridEncyptedEmail.ts @@ -1,3 +1,4 @@ +import { base64ToUint8Array, uint8ArrayToBase64 } from '../utils'; import { PublicKeys, PrivateKeys, HybridEncryptedEmail, Email, UserWithPublicKeys } from '../types'; import { encryptEmailContentSymmetrically, @@ -24,7 +25,8 @@ export async function encryptEmailHybrid( const aux = getAux(email.params); const { enc, encryptionKey } = await encryptEmailContentSymmetrically(email.body, aux, email.id); const encryptedKey = await encryptKeysHybrid(encryptionKey, recipient.publicKeys, senderPrivateKey); - return { enc, encryptedKey, recipientEmail: recipient.email, params: email.params, id: email.id }; + const encryptedText = uint8ArrayToBase64(enc); + return { enc: encryptedText, encryptedKey, recipientEmail: recipient.email, params: email.params, id: email.id }; } catch (error) { throw new Error('Failed to encrypt email with hybrid encryption', { cause: error }); } @@ -46,11 +48,18 @@ export async function encryptEmailHybridForMultipleRecipients( try { const aux = getAux(email.params); const { enc, encryptionKey } = await encryptEmailContentSymmetrically(email.body, aux, email.id); + const encryptedText = uint8ArrayToBase64(enc); const encryptedEmails: HybridEncryptedEmail[] = []; for (const recipient of recipients) { const encryptedKey = await encryptKeysHybrid(encryptionKey, recipient.publicKeys, senderPrivateKey); - encryptedEmails.push({ enc, encryptedKey, recipientEmail: recipient.email, params: email.params, id: email.id }); + encryptedEmails.push({ + enc: encryptedText, + encryptedKey, + recipientEmail: recipient.email, + params: email.params, + id: email.id, + }); } return encryptedEmails; } catch (error) { @@ -74,7 +83,8 @@ export async function decryptEmailHybrid( try { const aux = getAux(encryptedEmail.params); const encryptionKey = await decryptKeysHybrid(encryptedEmail.encryptedKey, senderPublicKeys, recipientPrivateKeys); - const body = await decryptEmailSymmetrically(encryptedEmail.enc, encryptionKey, aux); + const enc = base64ToUint8Array(encryptedEmail.enc); + const body = await decryptEmailSymmetrically(enc, encryptionKey, aux); return { body, params: encryptedEmail.params, id: encryptedEmail.id }; } catch (error) { throw new Error('Failed to decrypt email with hybrid encryption', { cause: error }); diff --git a/src/email-crypto/hybridEncyptedEmailAndSubject.ts b/src/email-crypto/hybridEncyptedEmailAndSubject.ts index 298c0eb..5e971e2 100644 --- a/src/email-crypto/hybridEncyptedEmailAndSubject.ts +++ b/src/email-crypto/hybridEncyptedEmailAndSubject.ts @@ -29,10 +29,11 @@ export async function encryptEmailAndSubjectHybrid( aux, email.id, ); + const encryptedText = uint8ArrayToBase64(enc); const encSubjectStr = uint8ArrayToBase64(subjectEnc); const encryptedKey = await encryptKeysHybrid(encryptionKey, recipient.publicKeys, senderPrivateKey); const params = { ...email.params, subject: encSubjectStr }; - return { enc, encryptedKey, recipientEmail: recipient.email, params, id: email.id }; + return { enc: encryptedText, encryptedKey, recipientEmail: recipient.email, params, id: email.id }; } catch (error) { throw new Error('Failed to encrypt the email and its subject with hybrid encryption', { cause: error }); } @@ -60,12 +61,13 @@ export async function encryptEmailAndSubjectHybridForMultipleRecipients( email.id, ); const encSubjectStr = uint8ArrayToBase64(subjectEnc); + const encryptedText = uint8ArrayToBase64(enc); const encryptedEmails: HybridEncryptedEmail[] = []; for (const recipient of recipients) { const encryptedKey = await encryptKeysHybrid(encryptionKey, recipient.publicKeys, senderPrivateKey); const params = { ...email.params, subject: encSubjectStr }; - encryptedEmails.push({ enc, encryptedKey, recipientEmail: recipient.email, params, id: email.id }); + encryptedEmails.push({ enc: encryptedText, encryptedKey, recipientEmail: recipient.email, params, id: email.id }); } return encryptedEmails; } catch (error) { @@ -92,12 +94,8 @@ export async function decryptEmailAndSubjectHybrid( const aux = getAuxWithoutSubject(encryptedEmail.params); const encryptionKey = await decryptKeysHybrid(encryptedEmail.encryptedKey, senderPublicKeys, recipientPrivateKeys); const encSubject = base64ToUint8Array(encryptedEmail.params.subject); - const { body, subject } = await decryptEmailAndSubjectSymmetrically( - encryptedEmail.enc, - encSubject, - encryptionKey, - aux, - ); + const enc = base64ToUint8Array(encryptedEmail.enc); + const { body, subject } = await decryptEmailAndSubjectSymmetrically(enc, encSubject, encryptionKey, aux); const params = { ...encryptedEmail.params, subject }; return { body, params, id: encryptedEmail.id }; } catch (error) { diff --git a/src/email-crypto/pwdProtectedEmail.ts b/src/email-crypto/pwdProtectedEmail.ts index 7365036..849476f 100644 --- a/src/email-crypto/pwdProtectedEmail.ts +++ b/src/email-crypto/pwdProtectedEmail.ts @@ -1,4 +1,5 @@ import { PwdProtectedEmail, Email } from '../types'; +import { base64ToUint8Array, uint8ArrayToBase64 } from '../utils'; import { encryptEmailContentSymmetrically, decryptEmailSymmetrically, @@ -21,8 +22,9 @@ export async function createPwdProtectedEmail(email: Email, password: string): P } const aux = getAux(email.params); const { enc, encryptionKey } = await encryptEmailContentSymmetrically(email.body, aux, email.id); + const encryptedText = uint8ArrayToBase64(enc); const encryptedKey = await passwordProtectKey(encryptionKey, password); - return { enc, encryptedKey, params: email.params, id: email.id }; + return { enc: encryptedText, encryptedKey, params: email.params, id: email.id }; } catch (error) { throw new Error('Failed to password-protect email', { cause: error }); } @@ -39,7 +41,8 @@ export async function decryptPwdProtectedEmail(encryptedEmail: PwdProtectedEmail try { const aux = getAux(encryptedEmail.params); const encryptionKey = await removePasswordProtection(encryptedEmail.encryptedKey, password); - const body = await decryptEmailSymmetrically(encryptedEmail.enc, encryptionKey, aux); + const enc = base64ToUint8Array(encryptedEmail.enc); + const body = await decryptEmailSymmetrically(enc, encryptionKey, aux); return { body, params: encryptedEmail.params, id: encryptedEmail.id }; } catch (error) { throw new Error('Failed to decrypt password-protect email', { cause: error }); diff --git a/src/email-crypto/pwdProtectedEmailAndSubject.ts b/src/email-crypto/pwdProtectedEmailAndSubject.ts index 3c198a4..c7f7487 100644 --- a/src/email-crypto/pwdProtectedEmailAndSubject.ts +++ b/src/email-crypto/pwdProtectedEmailAndSubject.ts @@ -27,10 +27,11 @@ export async function createPwdProtectedEmailAndSubject(email: Email, password: aux, email.id, ); + const encryptedText = uint8ArrayToBase64(enc); const encryptedKey = await passwordProtectKey(encryptionKey, password); const encSubjectStr = uint8ArrayToBase64(subjectEnc); const params = { ...email.params, subject: encSubjectStr }; - return { enc, encryptedKey, params, id: email.id }; + return { enc: encryptedText, encryptedKey, params, id: email.id }; } catch (error) { throw new Error('Failed to password-protect email and subject', { cause: error }); } @@ -51,12 +52,8 @@ export async function decryptPwdProtectedEmailAndSubject( const aux = getAuxWithoutSubject(encryptedEmail.params); const encryptionKey = await removePasswordProtection(encryptedEmail.encryptedKey, password); const encSubject = base64ToUint8Array(encryptedEmail.params.subject); - const { body, subject } = await decryptEmailAndSubjectSymmetrically( - encryptedEmail.enc, - encSubject, - encryptionKey, - aux, - ); + const enc = base64ToUint8Array(encryptedEmail.enc); + const { body, subject } = await decryptEmailAndSubjectSymmetrically(enc, encSubject, encryptionKey, aux); const params = { ...encryptedEmail.params, subject }; return { body, params, id: encryptedEmail.id }; } catch (error) { diff --git a/src/email-service/api-send.ts b/src/email-service/api-send.ts deleted file mode 100644 index 2d1b4d3..0000000 --- a/src/email-service/api-send.ts +++ /dev/null @@ -1,82 +0,0 @@ -import emailjs from '@emailjs/browser'; -import { EmailPublicParameters, HybridEncryptedEmail, PwdProtectedEmail } from '../types'; -import { hybridEncyptedEmailToBase64, pwdProtectedEmailToBase64 } from '../email-crypto/converters'; - -/** - * Email sender class - * - */ -export class EmailServiceAPI { - private readonly serviceId: string; - private readonly templateId: string; - private readonly publicKey: string; - - constructor(serviceId: string, templateId: string, publicKey: string) { - this.serviceId = serviceId; - this.templateId = templateId; - this.publicKey = publicKey; - } - - /** - * Sends an email to the server - * - * @param content - The email content (encrypted) - * @param param - The email public parameters - * @returns The server response - */ - async sendEmail(content: string, param: EmailPublicParameters) { - try { - const templateParams = { - from_email: param.sender.email, - from_name: param.sender.name, - to_email: param.recipient.email, - to_name: param.recipient.name, - email_subject: param.subject, - email_body: content, - timestamp: new Date().toISOString(), - }; - - await emailjs.send(this.serviceId, this.templateId, templateParams, this.publicKey); - } catch (error) { - throw new Error(`Failed to send an email ${(error as Error).message}`, { cause: error }); - } - } - - /** - * Sends a hybridly encrypted email to a particular recipient recipient - * - * @param encryptedEmail - The hybridly encrypted email - * @returns The server reply - */ - async sendHybridEmail(encryptedEmail: HybridEncryptedEmail) { - try { - if (encryptedEmail.recipientEmail !== encryptedEmail.params.recipient.email) { - throw new Error('Email is encrypted for another recipient'); - } - const body = hybridEncyptedEmailToBase64(encryptedEmail); - await this.sendEmail(body, encryptedEmail.params); - } catch (error) { - throw new Error(`Failed to email to the recipient: ${(error as Error).message}`, { cause: error }); - } - } - - /** - * Sends a password-protected email to a particular recipient - * - * @param protectedEmail - The password protected email - * @param recipient - The email recipient - * @returns The server reply - */ - async sendPwdProtectedEmail(protectedEmail: PwdProtectedEmail) { - try { - const body = pwdProtectedEmailToBase64(protectedEmail); - await this.sendEmail(body, protectedEmail.params); - } catch (error) { - throw new Error(`Failed to email to the recipient ${(error as Error).message}`, { cause: error }); - } - } -} - -export function getEmailServiceAPI(serviceId: string, templateId: string, publicKey: string): EmailServiceAPI { - return new EmailServiceAPI(serviceId, templateId, publicKey); -} diff --git a/src/email-service/index.ts b/src/email-service/index.ts deleted file mode 100644 index 2f62c7b..0000000 --- a/src/email-service/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './api-send'; diff --git a/src/index.ts b/src/index.ts index 51769eb..55fcfc6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,6 @@ export * as asymmetric from './asymmetric-crypto'; export * as deriveKey from './derive-key'; export * as emailCrypto from './email-crypto'; export * as emailSearch from './email-search'; -export * as emailService from './email-service'; export * as hash from './hash'; export * as keyWrapper from './key-wrapper'; export * as keystoreCrypto from './keystore-crypto'; diff --git a/src/types.ts b/src/types.ts index 5560845..6787d6f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -45,7 +45,7 @@ export type EmailKeysEncrypted = { export type HybridEncryptedEmail = { encryptedKey: HybridEncKey; - enc: Uint8Array; + enc: string; recipientEmail: string; params: EmailPublicParameters; id: string; @@ -53,7 +53,7 @@ export type HybridEncryptedEmail = { export type PwdProtectedEmail = { encryptedKey: PwdProtectedKey; - enc: Uint8Array; + enc: string; params: EmailPublicParameters; id: string; }; @@ -65,13 +65,13 @@ export type StoredEmail = { }; export type HybridEncKey = { - kyberCiphertext: Uint8Array; - encryptedKey: Uint8Array; + kyberCiphertext: string; + encryptedKey: string; }; export type PwdProtectedKey = { - encryptedKey: Uint8Array; - salt: Uint8Array; + encryptedKey: string; + salt: string; }; export type EmailBody = { diff --git a/tests/email-crypto/converter.test.ts b/tests/email-crypto/converter.test.ts index e3b4b1b..e376cbe 100644 --- a/tests/email-crypto/converter.test.ts +++ b/tests/email-crypto/converter.test.ts @@ -1,15 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { EmailBody, User, HybridEncKey, PwdProtectedKey } from '../../src/types'; -import { - emailBodyToBinary, - binaryToEmailBody, - pwdProtectedKeyToBase64, - base64ToPwdProtectedKey, - encHybridKeyToBase64, - base64ToEncHybridKey, - userToBase64, - base64ToUser, -} from '../../src/email-crypto'; +import { EmailBody } from '../../src/types'; +import { emailBodyToBinary, binaryToEmailBody } from '../../src/email-crypto'; describe('Test email crypto functions', () => { it('email converter to binary and back works', async () => { const email: EmailBody = { @@ -44,63 +35,4 @@ describe('Test email crypto functions', () => { /Failed to convert EmailBody to Uint8Array/, ); }); - - const alice: User = { - name: 'Alice', - email: 'alice@email.com', - }; - - it('user converter to base64 and back works', async () => { - const base64 = await userToBase64(alice); - const result = await base64ToUser(base64); - expect(result).toEqual(alice); - }); - - it('pwd protected key converter to base64 and back works', async () => { - const key: PwdProtectedKey = { - encryptedKey: new Uint8Array([1, 2, 3, 4, 5]), - salt: new Uint8Array([1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]), - }; - const base64 = pwdProtectedKeyToBase64(key); - const result = base64ToPwdProtectedKey(base64); - expect(result).toStrictEqual(key); - }); - - it('throws error if pwd protected key to base64 convertion fails', async () => { - const bad_key = { - salt: new Uint8Array([1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]), - }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(() => pwdProtectedKeyToBase64(bad_key as any)).toThrowError( - /Failed to convert password-protected key to base64/, - ); - }); - - it('throws error if base64 to pwd protected key convertion fails', async () => { - const bad_key = 'bad key'; - expect(() => base64ToPwdProtectedKey(bad_key)).toThrowError(/Failed to convert base64 to password-protected key/); - }); - - it('enc hybrid key converter to base64 and back works', async () => { - const key: HybridEncKey = { - encryptedKey: new Uint8Array([1, 2, 3, 4, 5]), - kyberCiphertext: new Uint8Array([1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]), - }; - const base64 = encHybridKeyToBase64(key); - const result = base64ToEncHybridKey(base64); - expect(result).toStrictEqual(key); - }); - - it('throws error if enc hybrid key to base64 convertion fails', async () => { - const bad_key = { - salt: new Uint8Array([1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]), - }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(() => encHybridKeyToBase64(bad_key as any)).toThrowError(/Failed to convert hybrid key to base64/); - }); - - it('throws error if base64 to enc hybrid key convertion fails', async () => { - const bad_key = 'bad key'; - expect(() => base64ToEncHybridKey(bad_key)).toThrowError(/Failed to convert base64 to hybrid key/); - }); }); diff --git a/tests/email-service/coreSend.test.ts b/tests/email-service/coreSend.test.ts deleted file mode 100644 index 4c91e53..0000000 --- a/tests/email-service/coreSend.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { getEmailServiceAPI } from '../../src/email-service/api-send'; -import { User, EmailPublicParameters } from '../../src/types'; - -describe('Check sending api', () => { - const userAlice: User = { email: 'alice email', name: 'alice' }; - const userBob: User = { email: 'bob email', name: 'bob' }; - const subject = 'test email subject'; - const emailBody = 'mock enc text $mock enc key'; - - const param: EmailPublicParameters = { - createdAt: new Date().toDateString(), - sender: userAlice, - recipient: userBob, - subject: subject, - }; - - it.skip('should send an email sucessfully', async () => { - // These env variables should be set in the .env.test file - const senderEmail = import.meta.env['VITE_EMAIL_SENDER']; - const recipientEmail = import.meta.env['VITE_EMAIL_RECIPIENT']; - const serverId = import.meta.env['VITE_SERVICE_ID']; - const templateID = import.meta.env['VITE_TEMPLATE_ID']; - const publicKey = import.meta.env['VITE_PUBLIC_KEY']; - - const senderService = getEmailServiceAPI(serverId, templateID, publicKey); - const sender: User = { email: senderEmail, name: 'Mock Sender Name' }; - const recipient: User = { email: recipientEmail, name: 'Mock Recipient Name' }; - const subject = 'Mock email subject'; - const emailBody = 'Mock encypted content for tests'; - const paramReam: EmailPublicParameters = { - createdAt: new Date().toDateString(), - sender, - recipient, - subject, - }; - - await expect(senderService.sendEmail(emailBody, paramReam)).resolves.toBeUndefined(); - }); - - it('Should throw an error if public keys is invalid', async () => { - const serverId = 'mock server id'; - const templateID = 'mock template id'; - const publicKey = 'invalid public key'; - - const sender = getEmailServiceAPI(serverId, templateID, publicKey); - await expect(sender.sendEmail(emailBody, param)).rejects.toThrowError(/Failed to send an email/); - }); -}); diff --git a/tests/email-service/sendingService.test.ts b/tests/email-service/sendingService.test.ts deleted file mode 100644 index a6714a7..0000000 --- a/tests/email-service/sendingService.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { describe, expect, it, vi, beforeEach } from 'vitest'; -import { - HybridEncryptedEmail, - Email, - EmailBody, - User, - PwdProtectedEmail, - EmailPublicParameters, -} from '../../src/types'; - -import { encryptEmailHybrid, createPwdProtectedEmail, generateEmailKeys } from '../../src/email-crypto'; -import { getEmailServiceAPI } from '../../src/email-service'; -import emailjs from '@emailjs/browser'; -import { generateUuid } from '../../src/utils'; - -vi.mock('@emailjs/browser', () => ({ - default: { - init: vi.fn(), - send: vi.fn(), - sendForm: vi.fn(), - }, -})); - -describe('Test sending email functions', async () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - const userAlice: User = { email: 'alice email', name: 'alice' }; - const userBob: User = { email: 'bob email', name: 'bob' }; - const service = getEmailServiceAPI('test-service-id', 'test-template-id', 'test-public-key'); - - const serviceId = 'test-service-id'; - const templateId = 'test-template-id'; - const emailServicePublicKey = 'test-public-key'; - - const emailBody: EmailBody = { - text: 'test body', - }; - const emailParams: EmailPublicParameters = { - createdAt: '2023-06-14T08:11:22.000Z', - labels: ['test label 1', 'test label2'], - sender: userAlice, - subject: 'test subject', - recipient: userBob, - replyToEmailID: generateUuid(), - }; - - const email: Email = { - body: emailBody, - params: emailParams, - id: generateUuid(), - }; - - const { privateKeys: alicePrivateKeys } = await generateEmailKeys(); - const { publicKeys: bobPublicKeys } = await generateEmailKeys(); - - const mockPassword = 'mock pwd'; - - const bobWithPublicKeys = { - ...userBob, - publicKeys: bobPublicKeys, - }; - - it('should sucessfully send hybrid email', async () => { - const encEmail: HybridEncryptedEmail = await encryptEmailHybrid(email, bobWithPublicKeys, alicePrivateKeys); - const spy = vi.spyOn(emailjs, 'send').mockResolvedValue({ status: 200, text: 'OK' }); - await service.sendHybridEmail(encEmail); - expect(spy).toHaveBeenCalled(); - expect(spy).toHaveBeenCalledWith( - serviceId, - templateId, - expect.objectContaining({ - from_email: userAlice.email, - from_name: userAlice.name, - to_email: userBob.email, - to_name: userBob.name, - email_subject: email.params.subject, - }), - emailServicePublicKey, - ); - }); - - it('should throw an error if recipient does not match expected one', async () => { - const encEmail: HybridEncryptedEmail = await encryptEmailHybrid(email, bobWithPublicKeys, alicePrivateKeys); - const wrongEmail = { ...encEmail, recipientEmail: 'another email' }; - await expect(service.sendHybridEmail(wrongEmail)).rejects.toThrow( - /Failed to email to the recipient: Email is encrypted for another recipient/, - ); - }); - - it('should throw an error if cannot send hybrid email', async () => { - const encEmail: HybridEncryptedEmail = await encryptEmailHybrid(email, bobWithPublicKeys, alicePrivateKeys); - vi.spyOn(emailjs, 'send').mockRejectedValue({ status: 404, text: 'Mocked Error' }); - await expect(service.sendHybridEmail(encEmail)).rejects.toThrow(/Failed to email to the recipient/); - }); - - it('should sucessfully send password protected email', async () => { - const encEmail: PwdProtectedEmail = await createPwdProtectedEmail(email, mockPassword); - - const spy = vi.spyOn(emailjs, 'send').mockResolvedValue({ status: 200, text: 'OK' }); - await service.sendPwdProtectedEmail(encEmail); - - expect(spy).toHaveBeenCalled(); - expect(spy).toHaveBeenCalledWith( - serviceId, - templateId, - expect.objectContaining({ - from_email: userAlice.email, - from_name: userAlice.name, - to_email: userBob.email, - to_name: userBob.name, - email_subject: email.params.subject, - }), - emailServicePublicKey, - ); - }); - - it('should throw an error if cannot send password protected email', async () => { - const encEmail: PwdProtectedEmail = await createPwdProtectedEmail(email, mockPassword); - - vi.spyOn(emailjs, 'send').mockRejectedValue({ status: 401, text: 'Mock Error' }); - - await expect(service.sendPwdProtectedEmail(encEmail)).rejects.toThrow(/Failed to email to the recipient/); - - vi.spyOn(emailjs, 'send').mockRejectedValue(new Error('Mock Error')); - await expect(service.sendPwdProtectedEmail(encEmail)).rejects.toThrow(/Failed to email to the recipient/); - }); -});