From cf7d5f9f55cd5f7c5612f3855d931ddda438b207 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Mon, 17 May 2021 12:26:04 +0800 Subject: [PATCH 01/12] refactor(crypto): minor refactor of decryptWithAttachments --- src/crypto.ts | 63 +++++++++++++++++++++++++++------------------- src/errors.ts | 14 +++++++++++ src/util/crypto.ts | 13 ++++++++++ 3 files changed, 64 insertions(+), 26 deletions(-) diff --git a/src/crypto.ts b/src/crypto.ts index af774b1..be94a6f 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -21,12 +21,13 @@ import { } from 'tweetnacl-util' import { determineIsFormFields } from './util/validate' -import { MissingPublicKeyError } from './errors' +import { MissingFilenameError, MissingPublicKeyError } from './errors' import { encryptMessage, decryptContent, verifySignedMessage, generateKeypair, + areFieldsValid, } from './util/crypto' export default class Crypto { @@ -206,7 +207,8 @@ export default class Crypto { * @param formSecretKey Secret key as a base-64 string * @param decryptParams The params containing encrypted content and information. * @returns A promise of the decrypted submission, including attachments (if any). Or else returns null if a decryption error decrypting any part of the submission. - * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content. + * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.] + * @throws {MissingFilenameError} if a file had an invalid name or if the name was not found */ decryptWithAttachments = async ( formSecretKey: string, @@ -215,7 +217,8 @@ export default class Crypto { const decryptedRecords: DecryptedAttachments = {} const filenames: Record = {} - const attachmentRecords: EncryptedAttachmentRecords = decryptParams.attachmentDownloadUrls ?? {} + const attachmentRecords: EncryptedAttachmentRecords = + decryptParams.attachmentDownloadUrls ?? {} const decryptedContent = this.decrypt(formSecretKey, decryptParams) if (decryptedContent === null) return null @@ -226,37 +229,45 @@ export default class Crypto { } }) - const downloadPromises : Array> = [] - for (let fieldId in attachmentRecords) { - // Original name for the file is not found - if (filenames[fieldId] === undefined) return null - - downloadPromises.push( - axios.get(attachmentRecords[fieldId], { responseType: 'json' }) - .then((downloadResponse) => { - const encryptedAttachment: EncryptedAttachmentContent = downloadResponse.data - const encryptedFile: EncryptedFileContent = { - submissionPublicKey: encryptedAttachment.encryptedFile.submissionPublicKey, - nonce: encryptedAttachment.encryptedFile.nonce, - binary: decodeBase64(encryptedAttachment.encryptedFile.binary), - } - - return this.decryptFile(formSecretKey, encryptedFile) - }).then((decryptedFile) => { - if (decryptedFile === null) throw new Error("Attachment decryption failed") - decryptedRecords[fieldId] = { filename: filenames[fieldId], content: decryptedFile } - })) + const fieldIds = Object.keys(attachmentRecords) + // Check if all fieldIds are within filenames + if (!areFieldsValid(fieldIds, filenames)) { + throw new MissingFilenameError() } try { - await Promise.all(downloadPromises) - } catch (e) { + await Promise.all( + fieldIds.map((fieldId) => { + return axios + .get(attachmentRecords[fieldId], { responseType: 'json' }) + .then((downloadResponse) => { + const encryptedAttachment: EncryptedAttachmentContent = + downloadResponse.data + const encryptedFile: EncryptedFileContent = { + submissionPublicKey: + encryptedAttachment.encryptedFile.submissionPublicKey, + nonce: encryptedAttachment.encryptedFile.nonce, + binary: decodeBase64(encryptedAttachment.encryptedFile.binary), + } + return this.decryptFile(formSecretKey, encryptedFile) + }) + .then((decryptedFile) => { + if (decryptedFile === null) + throw new Error('Attachment decryption failed') + decryptedRecords[fieldId] = { + filename: filenames[fieldId], + content: decryptedFile, + } + }) + }) + ) + } catch { return null } return { content: decryptedContent, - attachments: decryptedRecords + attachments: decryptedRecords, } } } diff --git a/src/errors.ts b/src/errors.ts index 518c65c..14a07e5 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -33,8 +33,22 @@ class WebhookAuthenticateError extends Error { } } +class MissingFilenameError extends Error { + constructor( + message = 'Some of the dowenloads failed due to missing filename. Please ensure that all files are named appropriately' + ) { + super(message) + this.name = this.constructor.name + + // Set the prototype explicitly. + // See https://github.com/facebook/jest/issues/8279 + Object.setPrototypeOf(this, MissingFilenameError.prototype) + } +} + export { MissingSecretKeyError, MissingPublicKeyError, WebhookAuthenticateError, + MissingFilenameError, } diff --git a/src/util/crypto.ts b/src/util/crypto.ts index 0062e45..7a11d22 100644 --- a/src/util/crypto.ts +++ b/src/util/crypto.ts @@ -79,3 +79,16 @@ export const verifySignedMessage = ( throw new Error('Failed to open signed message with given public key') return JSON.parse(encodeUTF8(openedMessage)) } + +/** + * Helper method to check if all the fields given are wtihin the filenames + * @param fieldIds the list of fieldIds to check + * @param filenames the filenames that should contain the fields + * @returns boolean indicating whether the fields are valid + */ +export const areFieldsValid = ( + fieldIds: string[], + filenames: Record +): boolean => { + return fieldIds.every((fieldId) => filenames[fieldId]) +} From f86ccbb138047124753c48f26c31abfb70b377c8 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Mon, 17 May 2021 12:33:47 +0800 Subject: [PATCH 02/12] refactor(crypto): removed custom error in favour of returning null --- src/crypto.ts | 4 ++-- src/errors.ts | 14 -------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/crypto.ts b/src/crypto.ts index be94a6f..06fac54 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -21,7 +21,7 @@ import { } from 'tweetnacl-util' import { determineIsFormFields } from './util/validate' -import { MissingFilenameError, MissingPublicKeyError } from './errors' +import { MissingPublicKeyError } from './errors' import { encryptMessage, decryptContent, @@ -232,7 +232,7 @@ export default class Crypto { const fieldIds = Object.keys(attachmentRecords) // Check if all fieldIds are within filenames if (!areFieldsValid(fieldIds, filenames)) { - throw new MissingFilenameError() + return null } try { diff --git a/src/errors.ts b/src/errors.ts index 14a07e5..518c65c 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -33,22 +33,8 @@ class WebhookAuthenticateError extends Error { } } -class MissingFilenameError extends Error { - constructor( - message = 'Some of the dowenloads failed due to missing filename. Please ensure that all files are named appropriately' - ) { - super(message) - this.name = this.constructor.name - - // Set the prototype explicitly. - // See https://github.com/facebook/jest/issues/8279 - Object.setPrototypeOf(this, MissingFilenameError.prototype) - } -} - export { MissingSecretKeyError, MissingPublicKeyError, WebhookAuthenticateError, - MissingFilenameError, } From c12984607fd1e731fef4834f96318921b9ce644f Mon Sep 17 00:00:00 2001 From: seaerchin Date: Mon, 17 May 2021 16:32:02 +0800 Subject: [PATCH 03/12] refactor(util/crypto): fixed docs and changed method name --- src/util/crypto.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/crypto.ts b/src/util/crypto.ts index 7a11d22..c7ee517 100644 --- a/src/util/crypto.ts +++ b/src/util/crypto.ts @@ -81,12 +81,12 @@ export const verifySignedMessage = ( } /** - * Helper method to check if all the fields given are wtihin the filenames + * Helper method to check if all the field IDs given are within the filenames * @param fieldIds the list of fieldIds to check * @param filenames the filenames that should contain the fields * @returns boolean indicating whether the fields are valid */ -export const areFieldsValid = ( +export const areAttachmentFieldIdsValid = ( fieldIds: string[], filenames: Record ): boolean => { From 0b320d10c3fa9b54b3dba7ec3b7dd4bf7359fd2e Mon Sep 17 00:00:00 2001 From: seaerchin Date: Mon, 17 May 2021 16:33:03 +0800 Subject: [PATCH 04/12] refactor(src/crypto): assigned promise to variable to reduce nesting; removed unused error in docs --- src/crypto.ts | 55 +++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/crypto.ts b/src/crypto.ts index 06fac54..248afc4 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -27,7 +27,7 @@ import { decryptContent, verifySignedMessage, generateKeypair, - areFieldsValid, + areAttachmentFieldIdsValid, } from './util/crypto' export default class Crypto { @@ -208,7 +208,6 @@ export default class Crypto { * @param decryptParams The params containing encrypted content and information. * @returns A promise of the decrypted submission, including attachments (if any). Or else returns null if a decryption error decrypting any part of the submission. * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.] - * @throws {MissingFilenameError} if a file had an invalid name or if the name was not found */ decryptWithAttachments = async ( formSecretKey: string, @@ -231,36 +230,36 @@ export default class Crypto { const fieldIds = Object.keys(attachmentRecords) // Check if all fieldIds are within filenames - if (!areFieldsValid(fieldIds, filenames)) { + if (!areAttachmentFieldIdsValid(fieldIds, filenames)) { return null } - try { - await Promise.all( - fieldIds.map((fieldId) => { - return axios - .get(attachmentRecords[fieldId], { responseType: 'json' }) - .then((downloadResponse) => { - const encryptedAttachment: EncryptedAttachmentContent = - downloadResponse.data - const encryptedFile: EncryptedFileContent = { - submissionPublicKey: - encryptedAttachment.encryptedFile.submissionPublicKey, - nonce: encryptedAttachment.encryptedFile.nonce, - binary: decodeBase64(encryptedAttachment.encryptedFile.binary), - } - return this.decryptFile(formSecretKey, encryptedFile) - }) - .then((decryptedFile) => { - if (decryptedFile === null) - throw new Error('Attachment decryption failed') - decryptedRecords[fieldId] = { - filename: filenames[fieldId], - content: decryptedFile, - } - }) + const downloadPromises = fieldIds.map((fieldId) => { + return axios + .get(attachmentRecords[fieldId], { responseType: 'json' }) + .then((downloadResponse) => { + const encryptedAttachment: EncryptedAttachmentContent = + downloadResponse.data + const encryptedFile: EncryptedFileContent = { + submissionPublicKey: + encryptedAttachment.encryptedFile.submissionPublicKey, + nonce: encryptedAttachment.encryptedFile.nonce, + binary: decodeBase64(encryptedAttachment.encryptedFile.binary), + } + return this.decryptFile(formSecretKey, encryptedFile) + }) + .then((decryptedFile) => { + if (decryptedFile === null) + throw new Error('Attachment decryption failed') + decryptedRecords[fieldId] = { + filename: filenames[fieldId], + content: decryptedFile, + } }) - ) + }) + + try { + await Promise.all(downloadPromises) } catch { return null } From 5350baedb5f8f5a95549c0ef642316df2c09d7c2 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Mon, 17 May 2021 16:35:41 +0800 Subject: [PATCH 05/12] docs(crypto): add docs --- src/crypto.ts | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/crypto.ts b/src/crypto.ts index 248afc4..776e032 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -207,7 +207,7 @@ export default class Crypto { * @param formSecretKey Secret key as a base-64 string * @param decryptParams The params containing encrypted content and information. * @returns A promise of the decrypted submission, including attachments (if any). Or else returns null if a decryption error decrypting any part of the submission. - * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.] + * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content. */ decryptWithAttachments = async ( formSecretKey: string, @@ -235,27 +235,31 @@ export default class Crypto { } const downloadPromises = fieldIds.map((fieldId) => { - return axios - .get(attachmentRecords[fieldId], { responseType: 'json' }) - .then((downloadResponse) => { - const encryptedAttachment: EncryptedAttachmentContent = - downloadResponse.data - const encryptedFile: EncryptedFileContent = { - submissionPublicKey: - encryptedAttachment.encryptedFile.submissionPublicKey, - nonce: encryptedAttachment.encryptedFile.nonce, - binary: decodeBase64(encryptedAttachment.encryptedFile.binary), - } - return this.decryptFile(formSecretKey, encryptedFile) - }) - .then((decryptedFile) => { - if (decryptedFile === null) - throw new Error('Attachment decryption failed') - decryptedRecords[fieldId] = { - filename: filenames[fieldId], - content: decryptedFile, - } - }) + return ( + axios + // Retrieve all the attachments as JSON + .get(attachmentRecords[fieldId], { responseType: 'json' }) + // Decrypt all the attachments + .then((downloadResponse) => { + const encryptedAttachment: EncryptedAttachmentContent = + downloadResponse.data + const encryptedFile: EncryptedFileContent = { + submissionPublicKey: + encryptedAttachment.encryptedFile.submissionPublicKey, + nonce: encryptedAttachment.encryptedFile.nonce, + binary: decodeBase64(encryptedAttachment.encryptedFile.binary), + } + return this.decryptFile(formSecretKey, encryptedFile) + }) + .then((decryptedFile) => { + if (decryptedFile === null) + throw new Error('Attachment decryption failed') + decryptedRecords[fieldId] = { + filename: filenames[fieldId], + content: decryptedFile, + } + }) + ) }) try { From 637f9eca0d248419cdae6a66e52a93e8ceae4668 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Tue, 18 May 2021 17:31:12 +0800 Subject: [PATCH 06/12] refactor(crypto): updated to avoid .then() chain --- src/crypto.ts | 38 +++++++++++++++++++++----------------- src/util/crypto.ts | 20 +++++++++++++++++++- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/crypto.ts b/src/crypto.ts index 776e032..53f31df 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -28,6 +28,8 @@ import { verifySignedMessage, generateKeypair, areAttachmentFieldIdsValid, + isNonEmptyArray, + convertEncryptedAttachmentToFileContent, } from './util/crypto' export default class Crypto { @@ -238,25 +240,27 @@ export default class Crypto { return ( axios // Retrieve all the attachments as JSON - .get(attachmentRecords[fieldId], { responseType: 'json' }) - // Decrypt all the attachments - .then((downloadResponse) => { - const encryptedAttachment: EncryptedAttachmentContent = - downloadResponse.data - const encryptedFile: EncryptedFileContent = { - submissionPublicKey: - encryptedAttachment.encryptedFile.submissionPublicKey, - nonce: encryptedAttachment.encryptedFile.nonce, - binary: decodeBase64(encryptedAttachment.encryptedFile.binary), - } - return this.decryptFile(formSecretKey, encryptedFile) + .get(attachmentRecords[fieldId], { + responseType: 'json', }) - .then((decryptedFile) => { - if (decryptedFile === null) + // Decrypt all the attachments + .then(async ({ data: downloadResponse }) => { + const encryptedFile = + convertEncryptedAttachmentToFileContent(downloadResponse) + + const decryptedFile = await this.decryptFile( + formSecretKey, + encryptedFile + ) + + // If there is content, extract and put + if (decryptedFile) { + decryptedRecords[fieldId] = { + filename: filenames[fieldId], + content: decryptedFile, + } + } else { throw new Error('Attachment decryption failed') - decryptedRecords[fieldId] = { - filename: filenames[fieldId], - content: decryptedFile, } }) ) diff --git a/src/util/crypto.ts b/src/util/crypto.ts index c7ee517..29d8aa6 100644 --- a/src/util/crypto.ts +++ b/src/util/crypto.ts @@ -1,7 +1,12 @@ import { encodeBase64, decodeBase64, encodeUTF8 } from 'tweetnacl-util' import nacl from 'tweetnacl' -import { Keypair, EncryptedContent } from '../types' +import { + Keypair, + EncryptedContent, + EncryptedAttachmentContent, + EncryptedFileContent, +} from '../types' /** * Helper method to generate a new keypair for encryption. @@ -92,3 +97,16 @@ export const areAttachmentFieldIdsValid = ( ): boolean => { return fieldIds.every((fieldId) => filenames[fieldId]) } + +/** + * Converts an encrypted attachment to encrypted file content + * @param encryptedAttachment The encrypted attachment + * @returns EncryptedFileContent The encrypted file content + */ +export const convertEncryptedAttachmentToFileContent = ( + encryptedAttachment: EncryptedAttachmentContent +): EncryptedFileContent => ({ + submissionPublicKey: encryptedAttachment.encryptedFile.submissionPublicKey, + nonce: encryptedAttachment.encryptedFile.nonce, + binary: decodeBase64(encryptedAttachment.encryptedFile.binary), +}) From 5ae3cbabee9ce54acfb8028d8fb492519794a112 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Tue, 18 May 2021 17:46:56 +0800 Subject: [PATCH 07/12] fix(crypto): removed unused import --- src/crypto.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/crypto.ts b/src/crypto.ts index 53f31df..f92bed4 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -28,7 +28,6 @@ import { verifySignedMessage, generateKeypair, areAttachmentFieldIdsValid, - isNonEmptyArray, convertEncryptedAttachmentToFileContent, } from './util/crypto' From 9c67a895ac543ba2e51089b08722355ee45a0c9f Mon Sep 17 00:00:00 2001 From: seaerchin Date: Wed, 19 May 2021 17:31:10 +0800 Subject: [PATCH 08/12] refactor(crypto): added custom error for decryption failure --- src/crypto.ts | 4 ++-- src/errors.ts | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/crypto.ts b/src/crypto.ts index f92bed4..a84dd69 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -21,7 +21,7 @@ import { } from 'tweetnacl-util' import { determineIsFormFields } from './util/validate' -import { MissingPublicKeyError } from './errors' +import { AttachmentDecryptionError, MissingPublicKeyError } from './errors' import { encryptMessage, decryptContent, @@ -259,7 +259,7 @@ export default class Crypto { content: decryptedFile, } } else { - throw new Error('Attachment decryption failed') + throw new AttachmentDecryptionError() } }) ) diff --git a/src/errors.ts b/src/errors.ts index 518c65c..89f379f 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -33,8 +33,19 @@ class WebhookAuthenticateError extends Error { } } +class AttachmentDecryptionError extends Error { + constructor(message = 'Attachment decryption with the given nonce failed.') { + super(message) + this.name = this.constructor.name + // Set the prototype explicitly. + // See https://github.com/facebook/jest/issues/8279 + Object.setPrototypeOf(this, AttachmentDecryptionError.prototype) + } +} + export { MissingSecretKeyError, MissingPublicKeyError, WebhookAuthenticateError, + AttachmentDecryptionError, } From b16fa1f88b14c2677edc4859cfe5a3000bd8032b Mon Sep 17 00:00:00 2001 From: seaerchin Date: Wed, 19 May 2021 17:31:32 +0800 Subject: [PATCH 09/12] test(utils): adds unit tests for areAttachmentFieldIdsValid --- spec/util.spec.ts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 spec/util.spec.ts diff --git a/spec/util.spec.ts b/spec/util.spec.ts new file mode 100644 index 0000000..657a683 --- /dev/null +++ b/spec/util.spec.ts @@ -0,0 +1,34 @@ +import { areAttachmentFieldIdsValid } from '../src/util/crypto' +describe('utils', () => { + describe('areAttachmentFieldIdsValid', () => { + it('should return true when all the fieldIds are within the filenames', () => { + // Arrange + const MOCK_FILENAMES = { + mock: 'file', + alsomock: 'file2', + } + const MOCK_FIELD_IDS = ['mock', 'alsomock'] + + // Act + const actual = areAttachmentFieldIdsValid(MOCK_FIELD_IDS, MOCK_FILENAMES) + + // Assert + expect(actual).toBe(true) + }) + + it('should return false when some fieldIds are not within the filenames', () => { + // Arrange + const MOCK_FILENAMES = { + mock: 'file', + alsomock: 'file2', + } + const MOCK_FIELD_IDS = ['mock', 'missingField'] + + // Act + const actual = areAttachmentFieldIdsValid(MOCK_FIELD_IDS, MOCK_FILENAMES) + + // Assert + expect(actual).toBe(false) + }) + }) +}) From 9802e3e2e8ff932c2d3531dc18c10e89d50f4722 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Thu, 27 May 2021 14:49:38 +0800 Subject: [PATCH 10/12] style(util/spec): updated constants to use Object.keys --- spec/util.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/util.spec.ts b/spec/util.spec.ts index 657a683..34636e0 100644 --- a/spec/util.spec.ts +++ b/spec/util.spec.ts @@ -7,7 +7,7 @@ describe('utils', () => { mock: 'file', alsomock: 'file2', } - const MOCK_FIELD_IDS = ['mock', 'alsomock'] + const MOCK_FIELD_IDS = Object.keys(MOCK_FILENAMES) // Act const actual = areAttachmentFieldIdsValid(MOCK_FIELD_IDS, MOCK_FILENAMES) @@ -22,7 +22,7 @@ describe('utils', () => { mock: 'file', alsomock: 'file2', } - const MOCK_FIELD_IDS = ['mock', 'missingField'] + const MOCK_FIELD_IDS = Object.keys(MOCK_FILENAMES).concat('missingField') // Act const actual = areAttachmentFieldIdsValid(MOCK_FIELD_IDS, MOCK_FILENAMES) From 0eedc38cbff44f40326f7a7f5e6bd0448e743fa6 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Thu, 27 May 2021 14:50:19 +0800 Subject: [PATCH 11/12] chore(errors): removed setting prototype explicitly --- src/errors.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/errors.ts b/src/errors.ts index 89f379f..69632df 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -37,9 +37,6 @@ class AttachmentDecryptionError extends Error { constructor(message = 'Attachment decryption with the given nonce failed.') { super(message) this.name = this.constructor.name - // Set the prototype explicitly. - // See https://github.com/facebook/jest/issues/8279 - Object.setPrototypeOf(this, AttachmentDecryptionError.prototype) } } From 9cce87932536dd3d0b9c7c8ae0fc91026c4bbb55 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Thu, 27 May 2021 14:50:41 +0800 Subject: [PATCH 12/12] style(crypto): removed mixing of promise chaining and async/await --- src/crypto.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/crypto.ts b/src/crypto.ts index a84dd69..a726e68 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -243,16 +243,13 @@ export default class Crypto { responseType: 'json', }) // Decrypt all the attachments - .then(async ({ data: downloadResponse }) => { + .then(({ data: downloadResponse }) => { const encryptedFile = convertEncryptedAttachmentToFileContent(downloadResponse) - - const decryptedFile = await this.decryptFile( - formSecretKey, - encryptedFile - ) - - // If there is content, extract and put + return this.decryptFile(formSecretKey, encryptedFile) + }) + .then((decryptedFile) => { + // Check if the file exists and set the filename accordingly; otherwise, throw an error if (decryptedFile) { decryptedRecords[fieldId] = { filename: filenames[fieldId],