Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions src/core/remote-client/remoteClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import CustomError from '../customError';
import * as fs from 'fs-extra';
import * as crypto from 'crypto';
import * as path from 'path';
import * as vscode from 'vscode';

const PASSWORDS_DIR = 'keys';
let masterPassword: string | undefined;

export interface ConnectOption {
// common
Expand Down Expand Up @@ -53,8 +60,40 @@ export default abstract class RemoteClient {
return this._doConnect(connectOption, config);
}

// Essayer de récupérer le mot de passe chiffré à partir du fichier
var decryptedPassword = "";
let shouldBreak = false;
while (true) {
masterPassword="";
decryptedPassword = await this.getDecryptedPassword(connectOption.host, config);
if (decryptedPassword !== "***")
break;

await vscode.window.showErrorMessage("Incorrect master password. Would you like to try again?", "Yes", "No").then((value) => {
if (value === "No")
shouldBreak = true;
});

if (shouldBreak)
break;
}


if (decryptedPassword && decryptedPassword !== "***")
{
var ret = this._doConnect({ ...connectOption, password: decryptedPassword }, config);
if (ret) {
return ret;
}
}

const password = await config.askForPasswd(`[${connectOption.host}]: Enter your password`);

//save the typed password
if (password !== undefined) {
await this.saveEncryptedPassword(connectOption.host, password, config);
}

// cancel connect
if (password === undefined) {
throw new CustomError(ErrorCode.CONNECT_CANCELLED, 'cancelled');
Expand All @@ -63,6 +102,63 @@ export default abstract class RemoteClient {
return this._doConnect({ ...connectOption, password }, config);
}

private async getDecryptedPassword(host: string, config: Config): Promise<string> {
const masterPasswordInput = await this.getMasterPassword(config);
const passwordsFile = path.join(PASSWORDS_DIR, `${host}.txt`);
if (!await fs.pathExists(passwordsFile)) {
return '';
}
const encryptedPassword = await fs.readFile(passwordsFile, 'utf8');
return this.decryptPassword(encryptedPassword, masterPasswordInput);
}

public async saveEncryptedPassword(host: string, password: string, config: Config): Promise<void> {
const masterPasswordInput = await this.getMasterPassword(config);
const encryptedPassword = this.encryptPassword(password, masterPasswordInput);

const passwordsFile = path.join(PASSWORDS_DIR, `${host}.txt`);
await fs.ensureDir(PASSWORDS_DIR);
await fs.writeFile(passwordsFile, encryptedPassword);
}


private encryptPassword(password: string, masterPassword: string): string {
try {
const cipher = crypto.createCipher('aes-256-cbc', masterPassword);
let encryptedPassword = cipher.update(password, 'utf8', 'hex');
encryptedPassword += cipher.final('hex');
return encryptedPassword;
} catch (error) {
console.error('Error encrypting password:', error);
return '';
}
}

private decryptPassword(encryptedPassword: string, masterPassword: string): string {
try {
const decipher = crypto.createDecipher('aes-256-cbc', masterPassword);
let decryptedPassword = decipher.update(encryptedPassword, 'hex', 'utf8');
decryptedPassword += decipher.final('utf8');
return decryptedPassword;
} catch (error) {
console.error('Error decrypting password:', error);
return '***';
}
}

private async getMasterPassword(config: Config): Promise<string> {
if (!masterPassword) {
const input = await config.askForPasswd('Please enter a master password:');
if (!input) {
throw new Error('The master password is required.');
}
masterPassword = input;
}
return masterPassword;
}



onDisconnected(cb) {
this._client
.on('end', () => {
Expand Down