From 72a10c453431a21fa4e44d307078ff13119d747d Mon Sep 17 00:00:00 2001 From: ayu-ch Date: Mon, 2 Jun 2025 23:28:35 +0530 Subject: [PATCH] fix: improve error handling in encryption/decryption functions Signed-off-by: ayu-ch --- .../uploadEncrypted/encryptionBrowser.ts | 45 ++++++++++++------- .../uploadEncrypted/encryptionNode.ts | 45 ++++++++++++------- src/errors/EncryptionErrors.ts | 43 ++++++++++++++++++ 3 files changed, 101 insertions(+), 32 deletions(-) create mode 100644 src/errors/EncryptionErrors.ts diff --git a/src/Lighthouse/uploadEncrypted/encryptionBrowser.ts b/src/Lighthouse/uploadEncrypted/encryptionBrowser.ts index 97d9afab..24a24583 100644 --- a/src/Lighthouse/uploadEncrypted/encryptionBrowser.ts +++ b/src/Lighthouse/uploadEncrypted/encryptionBrowser.ts @@ -1,3 +1,5 @@ +import { EncryptionError, DecryptionError, InvalidPasswordError, CorruptedDataError } from '../../errors/EncryptionErrors'; + /* istanbul ignore file */ declare const window: any const importKeyFromBytes = async (keyBytes: any) => @@ -50,14 +52,17 @@ export const encryptFile = async (fileArrayBuffer: any, password: any) => { return resultBytes } catch (error) { - console.error('Error encrypting file') - console.error(error) - throw error + console.error('Error encrypting file:', error); + throw new EncryptionError('Failed to encrypt file', error as Error); } } export const decryptFile = async (cipher: any, password: any) => { try { + if (!cipher || cipher.byteLength < 28) { + throw new CorruptedDataError('Invalid or corrupted encrypted data'); + } + const cipherBytes = new Uint8Array(cipher) const passwordBytes = new TextEncoder().encode(password) @@ -72,19 +77,27 @@ export const decryptFile = async (cipher: any, password: any) => { hash: 'SHA-256', }) - const decryptedContent = await window.crypto.subtle.decrypt( - { - name: 'AES-GCM', - iv: iv, - }, - aesKey, - data - ) - - return decryptedContent + try { + const decryptedContent = await window.crypto.subtle.decrypt( + { + name: 'AES-GCM', + iv: iv, + }, + aesKey, + data + ) + return decryptedContent + } catch (error: any) { + if (error.name === 'OperationError') { + throw new InvalidPasswordError(); + } + throw error; + } } catch (error) { - console.error('Error decrypting file') - console.error(error) - return + console.error('Error decrypting file:', error); + if (error instanceof DecryptionError) { + throw error; + } + throw new DecryptionError('Failed to decrypt file', error as Error); } } diff --git a/src/Lighthouse/uploadEncrypted/encryptionNode.ts b/src/Lighthouse/uploadEncrypted/encryptionNode.ts index c90aab29..cef09418 100644 --- a/src/Lighthouse/uploadEncrypted/encryptionNode.ts +++ b/src/Lighthouse/uploadEncrypted/encryptionNode.ts @@ -1,3 +1,5 @@ +import { EncryptionError, DecryptionError, InvalidPasswordError, CorruptedDataError } from '../../errors/EncryptionErrors'; + const importKeyFromBytes = async (keyBytes: any, crypto: any) => crypto.subtle.importKey('raw', keyBytes, 'PBKDF2', false, ['deriveKey']) @@ -55,9 +57,8 @@ const encryptFile = async (fileArrayBuffer: any, password: any) => { return resultBytes } catch (error) { - console.error('Error encrypting file') - console.error(error) - throw error + console.error('Error encrypting file:', error); + throw new EncryptionError('Failed to encrypt file', error as Error); } } @@ -66,6 +67,10 @@ const decryptFile = async (cipher: any, password: any) => { const { Crypto } = eval('require')('@peculiar/webcrypto') const crypto = new Crypto() + if (!cipher || cipher.byteLength < 28) { + throw new CorruptedDataError('Invalid or corrupted encrypted data'); + } + const cipherBytes = new Uint8Array(cipher) const passwordBytes = new TextEncoder().encode(password) @@ -85,20 +90,28 @@ const decryptFile = async (cipher: any, password: any) => { crypto ) - const decryptedContent = await crypto.subtle.decrypt( - { - name: 'AES-GCM', - iv: iv, - }, - aesKey, - data - ) - - return decryptedContent + try { + const decryptedContent = await crypto.subtle.decrypt( + { + name: 'AES-GCM', + iv: iv, + }, + aesKey, + data + ) + return decryptedContent + } catch (error: any) { + if (error.name === 'OperationError') { + throw new InvalidPasswordError(); + } + throw error; + } } catch (error) { - console.error('Error decrypting file') - console.error(error) - return + console.error('Error decrypting file:', error); + if (error instanceof DecryptionError) { + throw error; + } + throw new DecryptionError('Failed to decrypt file', error as Error); } } diff --git a/src/errors/EncryptionErrors.ts b/src/errors/EncryptionErrors.ts new file mode 100644 index 00000000..6ba55193 --- /dev/null +++ b/src/errors/EncryptionErrors.ts @@ -0,0 +1,43 @@ +export class DecryptionError extends Error { + readonly cause?: Error; + + constructor(message: string, cause?: Error | unknown) { + super(message); + this.name = 'DecryptionError'; + + if (cause instanceof Error) { + this.cause = cause; + } else if (cause !== undefined) { + this.cause = new Error(String(cause)); + } + } +} + +export class InvalidPasswordError extends DecryptionError { + constructor(message: string = 'Invalid password provided', cause?: Error | unknown) { + super(message, cause); + this.name = 'InvalidPasswordError'; + } +} + +export class CorruptedDataError extends DecryptionError { + constructor(message: string = 'Data appears to be corrupted', cause?: Error | unknown) { + super(message, cause); + this.name = 'CorruptedDataError'; + } +} + +export class EncryptionError extends Error { + readonly cause?: Error; + + constructor(message: string, cause?: Error | unknown) { + super(message); + this.name = 'EncryptionError'; + + if (cause instanceof Error) { + this.cause = cause; + } else if (cause !== undefined) { + this.cause = new Error(String(cause)); + } + } +} \ No newline at end of file