From b09b88a987f4de6800b95c341cff061c9ec814c1 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Fri, 3 Oct 2025 12:15:07 +0200 Subject: [PATCH 1/2] chore: bump sdk version --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4f317621..49c05fc0 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@inquirer/prompts": "7.8.6", "@internxt/inxt-js": "2.2.9", "@internxt/lib": "1.3.1", - "@internxt/sdk": "1.11.11", + "@internxt/sdk": "1.11.12", "@oclif/core": "4.5.4", "@oclif/plugin-autocomplete": "3.2.35", "axios": "1.12.2", diff --git a/yarn.lock b/yarn.lock index 9ce851f9..3cae546c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1534,10 +1534,10 @@ axios "^1.12.2" uuid "11.1.0" -"@internxt/sdk@1.11.11": - version "1.11.11" - resolved "https://registry.yarnpkg.com/@internxt/sdk/-/sdk-1.11.11.tgz#7fa44584b1bc6ab47fef6d138e228f509b449254" - integrity sha512-gq13+EtYWEW4BgCMJdspXCSLJR4U9JAdL78bVpCkZxv7UCyVlfxXI72ECsgk3CpT5b+/JmEMUAxX65AKOCLrVA== +"@internxt/sdk@1.11.12": + version "1.11.12" + resolved "https://registry.yarnpkg.com/@internxt/sdk/-/sdk-1.11.12.tgz#17077cf244e61406b2183a0b6ea380c944bab030" + integrity sha512-ubYhtGy5QyM4bLpu2us5QgONjC6VHBT7u2xuh5/MuUV0+F/A+qQlFPf2x7L+TGydGz91JOj10Th+EJ76gS5Rtg== dependencies: axios "^1.12.2" uuid "11.1.0" From 9f6fe4f16f39f5e89f7effb10ad01b247fac90d9 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Fri, 3 Oct 2025 12:16:22 +0200 Subject: [PATCH 2/2] feat: remove legacy code --- src/commands/download-file.ts | 9 +- src/services/auth.service.ts | 21 +--- src/services/config.service.ts | 7 +- src/services/keys.service.ts | 92 ---------------- src/types/command.types.ts | 28 ++++- src/types/keys.types.ts | 32 ------ test/fixtures/auth.fixture.ts | 32 +----- test/services/auth.service.test.ts | 13 +-- test/services/keys.service.test.ts | 168 +---------------------------- 9 files changed, 44 insertions(+), 358 deletions(-) diff --git a/src/commands/download-file.ts b/src/commands/download-file.ts index 8b0ee409..6f5099d1 100644 --- a/src/commands/download-file.ts +++ b/src/commands/download-file.ts @@ -7,12 +7,11 @@ import { CryptoService } from '../services/crypto.service'; import { DownloadService } from '../services/network/download.service'; import { SdkManager } from '../services/sdk-manager.service'; import { createWriteStream } from 'node:fs'; -import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; import { DriveFileItem } from '../types/drive.types'; import fs from 'node:fs/promises'; import path from 'node:path'; import { StreamUtils } from '../utils/stream.utils'; -import { NotValidDirectoryError, NotValidFileUuidError } from '../types/command.types'; +import { LoginUserDetails, NotValidDirectoryError, NotValidFileUuidError } from '../types/command.types'; import { ValidationService } from '../services/validation.service'; import { Environment } from '@internxt/inxt-js'; import { ConfigService } from '../services/config.service'; @@ -20,8 +19,8 @@ import { ConfigService } from '../services/config.service'; export default class DownloadFile extends Command { static readonly args = {}; static readonly description = - // eslint-disable-next-line max-len - 'Download and decrypts a file from Internxt Drive to a directory. The file name will be the same as the file name in your Drive.'; + 'Download and decrypts a file from Internxt Drive to a directory.' + + ' The file name will be the same as the file name in your Drive.'; static readonly aliases = ['download:file']; static readonly examples = ['<%= config.bin %> <%= command.id %>']; static readonly flags = { @@ -206,7 +205,7 @@ export default class DownloadFile extends Command { return downloadPath; }; - private prepareNetwork = async (user: UserSettings, jsonFlag?: boolean) => { + private prepareNetwork = async (user: LoginUserDetails, jsonFlag?: boolean) => { CLIUtils.doing('Preparing Network', jsonFlag); const networkModule = SdkManager.instance.getNetwork({ diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 8e505163..dcc63bb2 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -1,6 +1,5 @@ import { LoginDetails } from '@internxt/sdk'; import { SdkManager } from './sdk-manager.service'; -import { KeysService } from './keys.service'; import { CryptoService } from './crypto.service'; import { ConfigService } from './config.service'; import { @@ -30,27 +29,13 @@ export class AuthService { tfaCode: twoFactorCode, }; - const data = await authClient.login(loginDetails, CryptoService.cryptoProvider); + const data = await authClient.loginAccess(loginDetails, CryptoService.cryptoProvider); const { user, newToken } = data; - const { privateKey, publicKey } = user; - - const plainPrivateKeyInBase64 = privateKey - ? Buffer.from(KeysService.instance.decryptPrivateKey(privateKey, password)).toString('base64') - : ''; - - if (privateKey) { - await KeysService.instance.assertPrivateKeyIsValid(privateKey, password); - await KeysService.instance.assertValidateKeys( - Buffer.from(plainPrivateKeyInBase64, 'base64').toString(), - Buffer.from(publicKey, 'base64').toString(), - ); - } const clearMnemonic = CryptoService.instance.decryptTextWithKey(user.mnemonic, password); - const clearUser = { + const clearUser: LoginCredentials['user'] = { ...user, mnemonic: clearMnemonic, - privateKey: plainPrivateKeyInBase64, }; return { user: clearUser, @@ -118,7 +103,7 @@ export class AuthService { user: { ...newCreds.user, mnemonic: oldCreds.user.mnemonic, - privateKey: oldCreds.user.privateKey, + createdAt: new Date(newCreds.user.createdAt).toISOString(), }, token: newCreds.newToken, lastLoggedInAt: oldCreds.lastLoggedInAt, diff --git a/src/services/config.service.ts b/src/services/config.service.ts index f5d75dcc..a5ed7e9f 100644 --- a/src/services/config.service.ts +++ b/src/services/config.service.ts @@ -63,12 +63,7 @@ export class ConfigService { try { const encryptedCredentials = await fs.readFile(ConfigService.CREDENTIALS_FILE, 'utf8'); const credentialsString = CryptoService.instance.decryptText(encryptedCredentials); - const loginCredentials = JSON.parse(credentialsString, (key, value) => { - if (typeof value === 'string' && key === 'createdAt') { - return new Date(value); - } - return value; - }) as LoginCredentials; + const loginCredentials = JSON.parse(credentialsString) as LoginCredentials; return loginCredentials; } catch { return; diff --git a/src/services/keys.service.ts b/src/services/keys.service.ts index ba117e95..f24460c2 100644 --- a/src/services/keys.service.ts +++ b/src/services/keys.service.ts @@ -1,56 +1,10 @@ import { aes } from '@internxt/lib'; import * as openpgp from 'openpgp'; -import { - BadEncodedPrivateKeyError, - CorruptedEncryptedPrivateKeyError, - KeysDoNotMatchError, - WrongIterationsToEncryptPrivateKeyError, -} from '../types/keys.types'; import { CryptoUtils } from '../utils/crypto.utils'; export class KeysService { public static readonly instance: KeysService = new KeysService(); - /** - * Checks if a private key can be decrypted with a password, otherwise it throws an error - * @param privateKey The encrypted private key - * @param password The password used to encrypt the private key - * @throws {BadEncodedPrivateKeyError} If the PLAIN private key is base64 encoded (known issue introduced in the past) - * @throws {WrongIterationsToEncryptPrivateKeyError} If the ENCRYPTED private key was encrypted using the wrong iterations number (known issue introduced in the past) - * @throws {CorruptedEncryptedPrivateKeyError} If the ENCRYPTED private key is un-decryptable (corrupted) - * @async - */ - public assertPrivateKeyIsValid = async (privateKey: string, password: string): Promise => { - let privateKeyDecrypted: string | undefined; - - let badIterations = true; - try { - aes.decrypt(privateKey, password, 9999); - } catch { - badIterations = false; - } - if (badIterations === true) throw new WrongIterationsToEncryptPrivateKeyError(); - - let badEncrypted = false; - try { - privateKeyDecrypted = this.decryptPrivateKey(privateKey, password); - } catch { - badEncrypted = true; - } - - let hasValidFormat = false; - try { - if (privateKeyDecrypted !== undefined) { - hasValidFormat = await this.isValidKey(privateKeyDecrypted); - } - } catch { - /* no op */ - } - - if (badEncrypted === true) throw new CorruptedEncryptedPrivateKeyError(); - if (hasValidFormat === false) throw new BadEncodedPrivateKeyError(); - }; - /** * Encrypts a private key using a password * @param privateKey The plain private key @@ -71,52 +25,6 @@ export class KeysService { return aes.decrypt(privateKey, password); }; - /** - * Checks if a message encrypted with the public key can be decrypted with a private key, otherwise it throws an error - * @param privateKey The plain private key - * @param publicKey The plain public key - * @throws {KeysDoNotMatchError} If the keys can not be used together to encrypt/decrypt a message - * @async - **/ - public assertValidateKeys = async (privateKey: string, publicKey: string): Promise => { - const publicKeyArmored = await openpgp.readKey({ armoredKey: publicKey }); - const privateKeyArmored = await openpgp.readPrivateKey({ armoredKey: privateKey }); - - const plainMessage = 'validate-keys'; - const originalText = await openpgp.createMessage({ text: plainMessage }); - const encryptedMessage = await openpgp.encrypt({ - message: originalText, - encryptionKeys: publicKeyArmored, - }); - - const decryptedMessage = ( - await openpgp.decrypt({ - message: await openpgp.readMessage({ armoredMessage: encryptedMessage }), - verificationKeys: publicKeyArmored, - decryptionKeys: privateKeyArmored, - }) - ).data; - - if (decryptedMessage !== plainMessage) { - throw new KeysDoNotMatchError(); - } - }; - - /** - * Checks if a pgp key can be read - * @param key The openpgp key to be validated - * @returns True if it can be read, false otherwise - * @async - **/ - public isValidKey = async (key: string): Promise => { - try { - await openpgp.readKey({ armoredKey: key }); - return true; - } catch { - return false; - } - }; - /** * Generates pgp keys adding an AES-encrypted private key property by using a password * @param password The password for encrypting the private key diff --git a/src/types/command.types.ts b/src/types/command.types.ts index 994d760f..dc417ed8 100644 --- a/src/types/command.types.ts +++ b/src/types/command.types.ts @@ -1,7 +1,31 @@ -import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; +export interface LoginUserDetails { + userId: string; + uuid: string; + email: string; + name: string; + lastname: string; + username: string; + bridgeUser: string; + bucket: string; + rootFolderId: string; + mnemonic: string; + keys: { + ecc: { + publicKey: string; + privateKey: string; + }; + kyber: { + publicKey: string; + privateKey: string; + }; + }; + createdAt: string; + avatar: string | null; + emailVerified: boolean; +} export interface LoginCredentials { - user: UserSettings; + user: LoginUserDetails; token: string; lastLoggedInAt: string; lastTokenRefreshAt: string; diff --git a/src/types/keys.types.ts b/src/types/keys.types.ts index 9f0607f8..ceb86645 100644 --- a/src/types/keys.types.ts +++ b/src/types/keys.types.ts @@ -1,35 +1,3 @@ -export class BadEncodedPrivateKeyError extends Error { - constructor() { - super('Private key is bad encoded'); - - Object.setPrototypeOf(this, BadEncodedPrivateKeyError.prototype); - } -} - -export class WrongIterationsToEncryptPrivateKeyError extends Error { - constructor() { - super('Private key was encrypted using the wrong iterations number'); - - Object.setPrototypeOf(this, WrongIterationsToEncryptPrivateKeyError.prototype); - } -} - -export class CorruptedEncryptedPrivateKeyError extends Error { - constructor() { - super('Private key is corrupted'); - - Object.setPrototypeOf(this, CorruptedEncryptedPrivateKeyError.prototype); - } -} - -export class KeysDoNotMatchError extends Error { - constructor() { - super('Keys do not match'); - - Object.setPrototypeOf(this, KeysDoNotMatchError.prototype); - } -} - export interface AesInit { iv: string; salt: string; diff --git a/test/fixtures/auth.fixture.ts b/test/fixtures/auth.fixture.ts index 4d2d1287..16107dc7 100644 --- a/test/fixtures/auth.fixture.ts +++ b/test/fixtures/auth.fixture.ts @@ -1,7 +1,7 @@ -import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; import crypto from 'node:crypto'; +import { LoginUserDetails } from '../../src/types/command.types'; -export const UserFixture: UserSettings = { +export const UserFixture: LoginUserDetails = { userId: crypto.randomBytes(16).toString('hex'), uuid: crypto.randomBytes(16).toString('hex'), email: crypto.randomBytes(16).toString('hex'), @@ -10,21 +10,9 @@ export const UserFixture: UserSettings = { username: crypto.randomBytes(16).toString('hex'), bridgeUser: crypto.randomBytes(16).toString('hex'), bucket: crypto.randomBytes(16).toString('hex'), - backupsBucket: crypto.randomBytes(16).toString('hex'), - root_folder_id: crypto.randomInt(1, 9999), rootFolderId: crypto.randomBytes(16).toString('hex'), - rootFolderUuid: crypto.randomBytes(16).toString('hex'), - sharedWorkspace: false, - credit: crypto.randomInt(1, 9999), mnemonic: crypto.randomBytes(16).toString('hex'), - privateKey: crypto.randomBytes(16).toString('hex'), - publicKey: crypto.randomBytes(16).toString('hex'), - revocationKey: crypto.randomBytes(16).toString('hex'), - teams: false, - appSumoDetails: null, - registerCompleted: true, - hasReferralsProgram: false, - createdAt: new Date(), + createdAt: new Date().toISOString(), avatar: crypto.randomBytes(16).toString('hex'), emailVerified: true, keys: { @@ -39,7 +27,7 @@ export const UserFixture: UserSettings = { }, }; -export const UserSettingsFixture: UserSettings = { +export const UserSettingsFixture: LoginUserDetails = { userId: UserFixture.userId, email: UserFixture.email, name: UserFixture.name, @@ -47,20 +35,8 @@ export const UserSettingsFixture: UserSettings = { username: UserFixture.username, bridgeUser: UserFixture.bridgeUser, bucket: UserFixture.bucket, - backupsBucket: UserFixture.backupsBucket, - root_folder_id: UserFixture.root_folder_id, rootFolderId: UserFixture.rootFolderId, - rootFolderUuid: UserFixture.rootFolderUuid, - sharedWorkspace: UserFixture.sharedWorkspace, - credit: UserFixture.credit, mnemonic: UserFixture.mnemonic, - privateKey: UserFixture.privateKey, - publicKey: UserFixture.publicKey, - revocationKey: UserFixture.revocationKey, - teams: UserFixture.teams, - appSumoDetails: UserFixture.appSumoDetails, - registerCompleted: UserFixture.registerCompleted, - hasReferralsProgram: UserFixture.hasReferralsProgram, createdAt: UserFixture.createdAt, avatar: UserFixture.avatar, emailVerified: UserFixture.emailVerified, diff --git a/test/services/auth.service.test.ts b/test/services/auth.service.test.ts index a6195a67..abb9ad7e 100644 --- a/test/services/auth.service.test.ts +++ b/test/services/auth.service.test.ts @@ -2,7 +2,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import crypto from 'node:crypto'; import { Auth, LoginDetails, SecurityDetails } from '@internxt/sdk'; import { AuthService } from '../../src/services/auth.service'; -import { KeysService } from '../../src/services/keys.service'; import { CryptoService } from '../../src/services/crypto.service'; import { SdkManager } from '../../src/services/sdk-manager.service'; import { ConfigService } from '../../src/services/config.service'; @@ -16,6 +15,7 @@ import { } from '../../src/types/command.types'; import { UserCredentialsFixture } from '../fixtures/login.fixture'; import { fail } from 'node:assert'; +import { paths } from '@internxt/sdk/dist/schema'; describe('Auth service', () => { beforeEach(() => { @@ -28,14 +28,11 @@ describe('Auth service', () => { newToken: crypto.randomBytes(16).toString('hex'), user: UserFixture, userTeam: null, - }; + } as unknown as paths['/auth/cli/login/access']['post']['responses']['200']['content']['application/json']; const mockDate = new Date().toISOString(); - vi.spyOn(Auth.prototype, 'login').mockResolvedValue(loginResponse); + vi.spyOn(Auth.prototype, 'loginAccess').mockResolvedValue(loginResponse); vi.spyOn(SdkManager.instance, 'getAuth').mockReturnValue(Auth.prototype); - vi.spyOn(KeysService.instance, 'decryptPrivateKey').mockReturnValue(loginResponse.user.privateKey); - vi.spyOn(KeysService.instance, 'assertPrivateKeyIsValid').mockResolvedValue(); - vi.spyOn(KeysService.instance, 'assertValidateKeys').mockResolvedValue(); vi.spyOn(CryptoService.instance, 'decryptTextWithKey').mockReturnValue(loginResponse.user.mnemonic); vi.spyOn(Date.prototype, 'toISOString').mockReturnValue(mockDate); @@ -46,7 +43,7 @@ describe('Auth service', () => { ); const expectedResponseLogin: LoginCredentials = { - user: { ...loginResponse.user, privateKey: Buffer.from(loginResponse.user.privateKey).toString('base64') }, + user: { ...loginResponse.user }, token: loginResponse.newToken, lastLoggedInAt: mockDate, lastTokenRefreshAt: mockDate, @@ -61,7 +58,7 @@ describe('Auth service', () => { tfaCode: crypto.randomInt(1, 999999).toString().padStart(6, '0'), }; - const loginStub = vi.spyOn(Auth.prototype, 'login').mockRejectedValue(new Error('Login failed')); + const loginStub = vi.spyOn(Auth.prototype, 'loginAccess').mockRejectedValue(new Error('Login failed')); vi.spyOn(SdkManager.instance, 'getAuth').mockReturnValue(Auth.prototype); try { diff --git a/test/services/keys.service.test.ts b/test/services/keys.service.test.ts index d71232fc..6e3f20a1 100644 --- a/test/services/keys.service.test.ts +++ b/test/services/keys.service.test.ts @@ -1,12 +1,9 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import crypto from 'node:crypto'; -import { aes } from '@internxt/lib'; import * as openpgp from 'openpgp'; import { KeysService } from '../../src/services/keys.service'; import { ConfigService } from '../../src/services/config.service'; -import { AesInit, CorruptedEncryptedPrivateKeyError } from '../../src/types/keys.types'; -import { fail } from 'node:assert'; -import { Data } from '@openpgp/web-stream-tools'; +import { AesInit } from '../../src/types/keys.types'; vi.mock('openpgp', { spy: true }); @@ -20,169 +17,6 @@ describe('Keys service', () => { vi.restoreAllMocks(); }); - it('When public and private keys are validated, then there is no error thrown', async () => { - vi.spyOn(openpgp, 'readKey').mockResolvedValue({} as openpgp.Key); - vi.spyOn(openpgp, 'readPrivateKey').mockResolvedValue({} as openpgp.PrivateKey); - vi.spyOn(openpgp, 'createMessage').mockResolvedValue({} as openpgp.Message>); - vi.spyOn(openpgp, 'encrypt').mockResolvedValue({} as openpgp.Message>); - vi.spyOn(openpgp, 'readMessage').mockResolvedValue({} as openpgp.Message>); - vi.spyOn(openpgp, 'decrypt').mockResolvedValue({ data: 'validate-keys' } as openpgp.DecryptMessageResult & { - data: openpgp.MaybeStream; - }); - - await KeysService.instance.assertValidateKeys('dontcareprivate', 'dontcarepublic'); - }); - - it('When public and private keys are not valid, then the validation throws an error', async () => { - try { - await KeysService.instance.assertValidateKeys('privateKey', 'publickey'); - fail('Expected function to throw an error, but it did not.'); - } catch { - /* no op */ - } - }); - - it('When keys are not working good to encrypt/decrypt, then the validation throws an error', async () => { - vi.spyOn(openpgp, 'readKey').mockResolvedValue({} as openpgp.Key); - vi.spyOn(openpgp, 'readPrivateKey').mockResolvedValue({} as openpgp.PrivateKey); - vi.spyOn(openpgp, 'createMessage').mockResolvedValue({} as openpgp.Message>); - vi.spyOn(openpgp, 'encrypt').mockResolvedValue({} as openpgp.Message>); - vi.spyOn(openpgp, 'readMessage').mockResolvedValue({} as openpgp.Message>); - vi.spyOn(openpgp, 'decrypt').mockResolvedValue({ data: 'bad-validation' } as openpgp.DecryptMessageResult & { - data: openpgp.MaybeStream; - }); - //every dependency method mockResolvedValue (no error thrown), but nothing should be encrypted/decrypted, so the result should not be valid - try { - await KeysService.instance.assertValidateKeys('dontcareprivate', 'dontcarepublic'); - fail('Expected function to throw an error, but it did not.'); - } catch (err) { - const error = err as Error; - expect(error.message).to.be.equal('Keys do not match'); - } - }); - - it('When encryption fails, then it throws an error', async () => { - vi.spyOn(openpgp, 'readKey').mockResolvedValue({} as openpgp.Key); - vi.spyOn(openpgp, 'readPrivateKey').mockResolvedValue({} as openpgp.PrivateKey); - vi.spyOn(openpgp, 'createMessage').mockResolvedValue({} as openpgp.Message>); - vi.spyOn(openpgp, 'encrypt').mockRejectedValue(new Error('Encryption failed')); - vi.spyOn(openpgp, 'readMessage').mockResolvedValue({} as openpgp.Message>); - vi.spyOn(openpgp, 'decrypt').mockResolvedValue( - {} as openpgp.DecryptMessageResult & { - data: openpgp.MaybeStream; - }, - ); - - //encrypt method throws an exception as it can not encrypt the message (something with the encryptionKeys is bad) - try { - await KeysService.instance.assertValidateKeys('dontcareprivate', 'dontcarepublic'); - fail('Expected function to throw an error, but it did not.'); - } catch (err) { - const error = err as Error; - expect(error.message).to.be.equal('Encryption failed'); - } - }); - - it('When decryption fails, then it throws an error', async () => { - vi.spyOn(openpgp, 'readKey').mockResolvedValue({} as openpgp.Key); - vi.spyOn(openpgp, 'readPrivateKey').mockResolvedValue({} as openpgp.PrivateKey); - vi.spyOn(openpgp, 'createMessage').mockResolvedValue({} as openpgp.Message>); - vi.spyOn(openpgp, 'encrypt').mockResolvedValue({} as openpgp.Message>); - vi.spyOn(openpgp, 'readMessage').mockResolvedValue({} as openpgp.Message>); - vi.spyOn(openpgp, 'decrypt').mockRejectedValue(new Error('Decryption failed')); - - //decrypt method throws an exception as it can not decrypt the message (something with the decryptionKeys is bad) - try { - await KeysService.instance.assertValidateKeys('dontcareprivate', 'dontcarepublic'); - fail('Expected function to throw an error, but it did not.'); - } catch (err) { - const error = err as Error; - expect(error.message).to.be.equal('Decryption failed'); - } - }); - - it('When private key is encrypted with a password and it is validated, then there is no error thrown', async () => { - const plainPrivateKey = crypto.randomBytes(16).toString('hex'); - const password = crypto.randomBytes(8).toString('hex'); - - const encryptedPrivateKey = aes.encrypt(plainPrivateKey, password, aesInit); - - vi.spyOn(KeysService.instance, 'decryptPrivateKey').mockReturnValue(''); - vi.spyOn(KeysService.instance, 'isValidKey').mockResolvedValue(true); - - await KeysService.instance.assertPrivateKeyIsValid(encryptedPrivateKey, password); - }); - - it('When private key is encrypted with bad iterations, then it should throw an error', async () => { - const plainPrivateKey = crypto.randomBytes(16).toString('hex'); - const password = crypto.randomBytes(8).toString('hex'); - - const badEncryptionPrivateKey = aes.encrypt(plainPrivateKey, password, aesInit, 9999); - - vi.spyOn(KeysService.instance, 'decryptPrivateKey').mockImplementation(() => { - throw new Error(); - }); - vi.spyOn(KeysService.instance, 'isValidKey').mockResolvedValue(true); - - try { - await KeysService.instance.assertPrivateKeyIsValid(badEncryptionPrivateKey, password); - fail('Expected function to throw an error, but it did not.'); - } catch (err) { - const error = err as Error; - expect(error.message).to.be.equal('Private key was encrypted using the wrong iterations number'); - } - }); - - it('When private key is badly encrypted, then it throws a CorruptedEncryptedPrivateKey error', async () => { - const plainPrivateKey = crypto.randomBytes(16).toString('hex'); - const password = crypto.randomBytes(8).toString('hex'); - - const badEncryptionPrivateKey = aes.encrypt(plainPrivateKey, password, aesInit); - - vi.spyOn(KeysService.instance, 'decryptPrivateKey').mockImplementation(() => { - throw new CorruptedEncryptedPrivateKeyError(); - }); - vi.spyOn(KeysService.instance, 'isValidKey').mockResolvedValue(false); - - try { - await KeysService.instance.assertPrivateKeyIsValid(badEncryptionPrivateKey, password); - fail('Expected function to throw an error, but it did not.'); - } catch (err) { - const error = err as Error; - expect(error.message).to.be.equal('Private key is corrupted'); - } - }); - - it('When private key is bad encoded, then it throws a BadEncodedPrivateKey error', async () => { - const plainPrivateKey = crypto.randomBytes(16).toString('hex'); - const password = crypto.randomBytes(8).toString('hex'); - - const badEncodedPrivateKey = aes.encrypt(plainPrivateKey, password, aesInit); - - vi.spyOn(KeysService.instance, 'decryptPrivateKey').mockReturnValue(plainPrivateKey); - vi.spyOn(KeysService.instance, 'isValidKey').mockResolvedValue(false); - - try { - await KeysService.instance.assertPrivateKeyIsValid(badEncodedPrivateKey, password); - fail('Expected function to throw an error, but it did not.'); - } catch (err) { - const error = err as Error; - expect(error.message).to.be.equal('Private key is bad encoded'); - } - }); - - it('When the key is not valid, then isValid method returns false', async () => { - vi.spyOn(openpgp, 'readKey').mockRejectedValue(new Error()); - - expect(await KeysService.instance.isValidKey('key')).to.be.equal(false); - }); - - it('When the key is valid, then isValid method returns true', async () => { - vi.spyOn(openpgp, 'readKey').mockResolvedValue({} as openpgp.Key); - - expect(await KeysService.instance.isValidKey('key')).to.be.equal(true); - }); - it('When message is encrypted with private key & password, then it can be decrypted using same data', async () => { const plainPrivateKey = crypto.randomBytes(16).toString('hex'); const password = crypto.randomBytes(8).toString('hex');